home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
muds
/
pennmush.000
/
pennmush-1.50-p8-linux.tar
/
pennmush
/
eval.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-10-22
|
105KB
|
5,011 lines
/* eval.c */
#include "copyright.h"
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <sys/types.h>
#include "config.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "globals.h"
#ifdef MEM_CHECK
#include "mem_check.h"
#endif
#define FUNCTION(x) \
static void x(buff, args, nargs, privs, doer) \
char *buff; \
char *args[10]; \
int nargs; \
dbref privs; \
dbref doer;
#define GLOBAL_FUN(x) \
static void x(buff, args, nargs, privs, doer, fn_num) \
char *buff; \
char *args[10]; \
int nargs; \
dbref privs; \
dbref doer; \
int fn_num;
#ifdef FLOATING_POINTS
#define aton atof
#else
#define aton atoi
#endif /* FLOATING_POINTS */
/*
* privs == player == %!
* doer == cause == %N (enactor)
*/
#define FN_REG 0
#define FN_NOPARSE 1
#define MAX_GLOBAL_FNS 50
#define GLOBAL_OFFSET 100
#define GF_Index(x) (x - GLOBAL_OFFSET)
/* excessive recursion prevention */
#define MAX_NEST_LEVEL 20
static int recurs_lev = 0;
int invok_counter = 0;
/* functions not found in this file */
extern void fun_idlesecs(); /* bsd.c */
extern void fun_conn(); /* bsd.c */
extern void fun_lattr(); /* attrib.c */
extern void fun_lsearch(); /* wiz.c */
extern void fun_lstats(); /* wiz.c */
extern void fun_lwho(); /* bsd.c */
#ifdef USE_MAILER
extern void fun_mail(); /* mail.c */
#endif /* USE_MAILER */
static void fun_gfun(); /* for later reference */
char rptr[10][BUFFER_LEN]; /* local registers */
#define ALPHANUM_LIST 0
#define NUMERIC_LIST 1
#define DBREF_LIST 2
/* -------------------------------------------------------------------------*
* Utilities.
*/
dbref match_thing(player, name)
dbref player;
const char *name;
{
init_match(player, name, NOTYPE);
match_everything();
return (noisy_match_result());
}
#ifdef FLOATING_POINTS
static void fval(buff, num)
char *buff;
double num;
{
/* Copy a floating-point value into a buffer, removing trailing zeros
* and a possible ending '.'
*/
char *p;
sprintf(buff, "%.6f", num);
/* zap extra trailing zeros. */
if ((p = (char *) rindex(buff, '0')) == NULL)
return;
else if (*(p+1) == '\0') {
while (*p == '0')
*p-- = '\0';
}
/* zap trailing '.' */
p = (char *) rindex(buff, '.');
if (*(p+1) == '\0')
*p = '\0';
}
#else
#define fval(b,n) sprintf(b, "%d", n)
#endif /* FLOATING_POINTS */
static int ok_nargs(fname, nargs, min, max, buff)
const char *fname;
int nargs;
int min;
int max;
char *buff;
{
/* Check the number of args passed to a varargs function, make sure it
* falls within the correct range.
*/
if ((nargs >= min) && (nargs <= max))
return 1;
if (max == min+1)
sprintf(buff, "#-1 FUNCTION (%s) EXPECTS %d OR %d ARGUMENTS",
fname, min, max);
else
sprintf(buff, "#-1 FUNCTION (%s) EXPECTS BETWEEN %d AND %d ARGUMENTS",
fname, min, max);
return 0;
}
static int list2arr(r, max, list)
char *r[];
int max;
char *list;
{
/* chops up a list of words into an array of words. The list is
* destructively modified.
*/
char *p;
int i;
for (i = 0, p = (char *) strtok(list, " ");
(p != NULL) && (i < max);
i++, p = (char *) strtok(NULL, " "))
r[i] = p;
return i;
}
static void arr2list(r, max, list)
char *r[];
int max;
char *list;
{
int i;
char *bp = list;
safe_str(r[0], list, &bp);
for (i = 1; i < max; i++) {
safe_chr(' ', list, &bp);
safe_str(r[i], list, &bp);
}
*bp = '\0';
}
static int dbnum(str)
char *str;
{
if ((strlen(str) < 2) && (*str != '#'))
return 0;
else
return atoi(str+1);
}
static int autodetect_list(ptrs, nptrs)
char *ptrs[];
int nptrs;
{
int sort_type, i;
char *p;
sort_type = NUMERIC_LIST;
for (i = 0; i < nptrs; i++) {
switch (sort_type) {
case NUMERIC_LIST:
if (!is_number(ptrs[i])) {
/* If we get something non-numeric, switch to an
* alphanumeric guess, unless this is the first
* element and we have a dbref.
*/
if (i == 0) {
p = ptrs[i];
if (*p++ != NUMBER_TOKEN)
return ALPHANUM_LIST;
else if (is_number(p))
sort_type = DBREF_LIST;
else
return ALPHANUM_LIST;
} else {
return ALPHANUM_LIST;
}
}
break;
case DBREF_LIST:
/* If what we get following the '#' sign isn't a number,
* we sort on alphanumeric.
*/
p = ptrs[i];
if (*p++ != NUMBER_TOKEN)
return ALPHANUM_LIST;
if (!is_number(p))
return ALPHANUM_LIST;
break;
default:
return ALPHANUM_LIST;
}
}
return sort_type;
}
static int get_list_type(args, nargs, type_pos, ptrs, nptrs)
char *args[];
int nargs;
int type_pos;
char *ptrs[];
int nptrs;
{
if (nargs >= type_pos) {
switch (tolower(*args[type_pos - 1])) {
case 'a':
return ALPHANUM_LIST;
case 'd':
return DBREF_LIST;
case 'n':
return NUMERIC_LIST;
case '\0':
return autodetect_list(ptrs, nptrs);
default:
return ALPHANUM_LIST;
}
}
return autodetect_list(ptrs, nptrs);
}
/* -------------------------------------------------------------------------*
All function declarations follow the format
static void fun_<name>(char *buff, char *args[10], dbref privs, dbref doer)
All results are returned in buff. Be careful not to overflow this buffer.
args are the arguments passed to the function. There cannot be more than
ten. They can be hacked up if necessary, but for ease in debugging, it's
probably a better idea not to.
privs is the object executing the function.
doer is the enactor (the thing which triggered privs).
*--------------------------------------------------------------------------*/
/* --------------------------------------------------------------------------
* Utility functions: RAND, DIE, SECURE, SPACE, BEEP, SWITCH, EDIT,
* ITER, ESCAPE, SQUISH
*/
FUNCTION(fun_rand)
{
/*
* Uses Sh'dow's random number generator, found in utils.c. Better
* distribution than original, w/ minimal speed losses.
*/
sprintf(buff, "%d", getrandom(atoi(args[0])));
}
FUNCTION(fun_die)
{
int n = atoi(args[0]);
int die = atoi(args[1]);
int count;
int total = 0;
if ((n < 1) || (n > 20)) {
strcpy(buff, "#-1 NUMBER OUT OF RANGE");
return;
}
for (count = 0; count < n; count++)
total += getrandom(die) + 1;
sprintf(buff, "%d", total);
}
FUNCTION(fun_secure)
{
/* this function smashes all occurences of "unsafe" characters in a string.
* "unsafe" characters are ( ) [ ] { } $ % , ; \
* these characters get replaced by spaces
*/
strcpy(buff, args[0]);
while (*buff) {
switch (*buff) {
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '$':
case '%':
case ',':
case ';':
case '\\':
*buff = ' ';
break;
}
buff++;
}
}
FUNCTION(fun_escape)
{
/* another function more or less right out of 2.0 code */
char *s, *p;
s = args[0];
p = buff;
while (*s) {
switch (*s) {
case '%':
case '\\':
case '[':
case ']':
case '{':
case '}':
case ';':
safe_chr('\\', buff, &p);
default:
if (p == buff)
safe_chr('\\', buff, &p);
safe_chr(*s, buff, &p);
}
s++;
}
*p = '\0';
}
FUNCTION(fun_squish)
{
/* zaps leading and trailing spaces, and reduces other spaces to a single
* space. This only applies to the literal space character, and not to
* tabs, newlines, etc.
* We do not need to check for buffer length overflows, since we're
* never going to end up with a longer string.
*/
char *bp, *tp;
bp = buff;
for (tp = args[0]; *tp == ' '; tp++) /* skip leading spaces */
;
while (*tp) {
while (*tp && (*tp != ' ')) /* copy non-spaces */
*bp++ = *tp++;
if (!*tp) { /* end of string */
*bp = '\0';
return;
}
/* we've hit a space. Copy it, then skip to the next non-space. */
*bp++ = *tp++;
while (*tp && (*tp == ' '))
tp++;
}
/* we might have to get rid of trailing spaces. Just overwrite them with
* nulls. Make sure we're not at the beginning of the string, though.
*/
if ((bp != buff) && (*(tp -1) == ' ')) {
bp--;
while ((bp != buff) && (*bp == ' '))
*bp-- = '\0';
} else {
*bp = '\0';
}
}
FUNCTION(fun_space)
{
char tbuf1[BUFFER_LEN];
int i;
int s = atoi(args[0]);
if (s <= 0) {
*buff = '\0';
return;
}
if (s > BUFFER_LEN - 3)
s = BUFFER_LEN - 3;
for (i = 0; i < s; i++)
tbuf1[i] = ' ';
tbuf1[i] = '\0';
strcpy(buff, tbuf1);
}
FUNCTION(fun_beep)
{
char tbuf1[10];
int i, k;
/* this function prints 1 to 5 beeps. The alert character '\a' is
* an ANSI C invention; non-ANSI-compliant implementations may ignore
* the '\' character and just print an 'a', or do something else nasty.
*/
k = atoi(args[0]);
if (!Hasprivs(privs) || (k <= 0) || (k > 5)) {
strcpy(buff, "#-1 PERMISSION DENIED");
return;
}
for (i = 0; i < k; i++)
tbuf1[i] = '\a';
tbuf1[i] = '\0';
strcpy(buff, tbuf1);
}
#define FreeArgs \
if (mstr) { free(mstr); mstr = NULL; } \
if (curr) { free(curr); curr = NULL; } \
if (rbuf) { free(rbuf); }
FUNCTION(fun_switch)
{
/* this works a bit like the @switch command: it returns the string
* appropriate to the match. It picks the first match, like @select
* does, though.
* Args to this function are passed unparsed. Args are not evaluated
* until they are needed.
*/
int i;
char *extra[90];
int sw_args;
char *mstr, *curr, *rbuf;
mstr = curr = rbuf = NULL;
/* need at least two args */
if (!args[1]) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
return;
}
mstr = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[0]);
/* try matching, return match immediately when found */
for (i = 1; (i < 9) && args[i] && args[i + 1]; i+= 2) {
curr = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[i]);
if (local_wild_match(curr, mstr)) {
rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK,
args[i+1]);
strcpy(buff, rbuf);
FreeArgs;
return;
}
if (curr) {
free(curr);
curr = NULL;
}
}
/* if nothing's been matched, our counter is at the default. Or, we
* could be at the tenth arg. If we are, we want to split up the
* tenth argument. It's already been parsed.
*/
if ((i == 9) && args[9]) {
sw_args = extra_arglist(args[9], extra, 90);
if (sw_args < 2) {
/* we don't have an overflow. Just return the arg. */
rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[9]);
strcpy(buff, rbuf);
FreeArgs;
return;
}
/* now we try matching just like before */
for (i = 0; (i < sw_args) && extra[i] && extra[i + 1]; i += 2) {
curr = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, extra[i]);
if (local_wild_match(curr, mstr)) {
rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK,
extra[i+1]);
strcpy(buff, rbuf);
FreeArgs;
return;
}
if (curr) {
free(curr);
curr = NULL;
}
}
/* our counter is at the default. If there is one, return it. */
if ((i < 90) && extra[i]) {
rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, extra[i]);
strcpy(buff, rbuf);
FreeArgs;
return;
}
} else if ((i < 10) && args[i]) {
/* no extra arguments, just copy the default */
rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[i]);
strcpy(buff, rbuf);
FreeArgs;
return;
}
/* otherwise, return nothing */
FreeArgs;
*buff = '\0';
}
#undef FreeArgs
FUNCTION(fun_edit)
{
int i, len;
char *str, *f, *r;
str = args[0]; /* complete string */
f = args[1]; /* find this */
r = args[2]; /* replace it with this */
if (!*f && !*r) { /* check for nothing, or we'll infinite loop */
strcpy(buff, str);
return;
}
if (!strcmp(f, "$")) {
/* append */
if (strlen(str) + strlen(r) < BUFFER_LEN) {
strcpy(buff, str);
strcat(buff, r);
} else {
strcpy(buff, "#-1 STRING TOO LONG");
}
} else if (!strcmp(f, "^")) {
/* prepend */
if (strlen(str) + strlen(r) < BUFFER_LEN) {
strcpy(buff, r);
strcat(buff, str);
} else {
strcpy(buff, "#-1 STRING TOO LONG");
}
} else {
/* find and replace */
len = strlen(f);
for (i = 0; (i < BUFFER_LEN) && *str; )
if (strncmp(f, str, len) == 0) {
if ((i + strlen(r)) < BUFFER_LEN) {
strcpy(buff + i, r);
i += strlen(r);
str += len;
} else
buff[i++] = *str++;
} else
buff[i++] = *str++;
buff[i++] = '\0';
}
}
FUNCTION(fun_iter)
{
/* more or less straight from the TinyMUSH 2.0 code.
* There is one big difference -- arguments to this function are UNPARSED.
*/
char *curr, *objstr, *tbuf, *result, *bp;
char *s = NULL;
s = curr = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[0]);
bp = buff;
while (curr && *curr) {
while (isspace(*curr))
curr++;
if (*curr) {
objstr = parse_to(&curr, ' ', EV_STRIP);
tbuf = replace_string("##", objstr, args[1]);
result = exec(privs, doer, EV_FCHECK, tbuf);
free(tbuf);
if (buff != bp)
safe_chr(' ', buff, &bp);
safe_str(result, buff, &bp);
free(result);
}
}
*bp = '\0';
if (s)
free(s);
}
/* --------------------------------------------------------------------------
* Time functions: TIME, SECS, CONVTIME, CONVSECS
*/
FUNCTION(fun_time)
{
time_t tt;
tt = time((time_t *) 0);
sprintf(buff, "%s", ctime(&tt));
buff[strlen(buff)-1] = '\0';
}
FUNCTION(fun_secs)
{
time_t tt;
time(&tt);
strcpy(buff, tprintf("%d", tt));
}
FUNCTION(fun_convsecs)
{
/* converts seconds to a time string */
time_t tt;
tt = atol(args[0]);
sprintf(buff, "%s", ctime(&tt));
buff[strlen(buff)-1] = '\0';
}
static const char *month_table[] =
{
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
};
int do_convtime(str, ttm)
char *str;
struct tm *ttm;
{
/* converts time string to a struct tm. Returns 1 on success, 0 on fail.
* Time string format is always 24 characters long, in format
* Ddd Mmm DD HH:MM:SS YYYY
*/
char buf[32];
char *p, *q;
int i;
if (strlen(str) != 24)
return 0;
/* do not hack up the string we were passed */
strcpy(buf, str);
/* move over the day of week and truncate. Will always be 3 chars.
* we don't need this, so we can ignore it.
*/
p = (char *) index(buf, ' ');
if (p)
*p++ = '\0';
if (strlen(buf) != 3)
return 0;
/* get the month (3 chars), and convert it to a number */
q = (char *) index(p, ' ');
if (q)
*q++ = '\0';
if (strlen(p) != 3)
return 0;
else {
for (i = 0; (i < 12) && strcmp(month_table[i], p); i++)
;
if (i == 12) /* not found */
return 0;
else
ttm->tm_mon = i;
}
/* get the day of month */
p = q;
while (isspace(*p)) /* skip leading space */
p++;
q = (char *) index(p, ' ');
if (q)
*q++ = '\0';
ttm->tm_mday = atoi(p);
/* get hours */
p = (char *) index(q, ':');
if (p)
*p++ = '\0';
ttm->tm_hour = atoi(q);
/* get minutes */
q = (char *) index(p, ':');
if (q)
*q++ = '\0';
ttm->tm_min = atoi(p);
/* get seconds */
p = (char *) index(q, ' ');
if (p)
*p++ = '\0';
ttm->tm_sec = atoi(q);
/* get year */
ttm->tm_year = atoi(p) - 1900;
return 1;
}
FUNCTION(fun_convtime)
{
/* converts time string to seconds */
struct tm *ttm;
ttm = (struct tm *) malloc(sizeof(struct tm));
if (do_convtime(args[0], ttm)) {
#ifdef SUN_OS
sprintf(buff, "%d", timelocal(ttm));
#else
sprintf(buff, "%d", mktime(ttm));
#endif /* SUN_OS */
} else {
strcpy(buff, "-1");
}
free(ttm);
}
/* --------------------------------------------------------------------------
* Attribute functions: GET, XGET, V, S, UFUN, ZFUN
*/
char *do_get_attrib(player, thing, attrib)
dbref player;
dbref thing;
char *attrib;
{
ATTR *a;
a = atr_get(thing, upcasestr(attrib));
if (!a) {
return ((char *) ""); /* used to be #-1 NO SUCH ATTRIBUTE */
} else {
if (Can_Read_Attr(player, thing, a)) {
if(strlen(uncompress(a->value)) < BUFFER_LEN)
return ((char *) uncompress(a->value));
else
return ((char *) "#-1 ATTRIBUTE LENGTH TOO LONG");
} else
return ((char *) "#-1 NO PERMISSION TO GET ATTRIBUTE");
}
return ((char *) ""); /* NOTREACHED */
}
FUNCTION(fun_get)
{
dbref thing;
char *s;
char tbuf1[BUFFER_LEN];
strcpy(tbuf1, args[0]);
for(s = tbuf1; *s && (*s != '/'); s++);
if(!*s) {
strcpy(buff, "#-1 BAD ARGUMENT FORMAT TO GET");
return;
}
*s++ = 0;
thing = match_thing(privs, tbuf1);
if(thing == NOTHING) {
strcpy(buff, "#-1 NO SUCH OBJECT VISIBLE");
return;
}
if(*s == '_') s++;
if(!*s) {
strcpy(buff, "#-1 BAD ARGUMENT FORMAT TO GET");
return;
}
strcpy(buff, do_get_attrib(privs, thing, s));
}
/* Functions like get, but uses the standard way of passing arguments */
/* to a function, and thus doesn't choke on nested functions within. */
FUNCTION(fun_xget)
{
dbref thing;
thing = match_thing(privs, args[0]);
if (thing == NOTHING) {
strcpy(buff, "#-1 NO SUCH OBJECT VISIBLE");
return;
}
strcpy(buff, do_get_attrib(privs, thing, args[1]));
}
FUNCTION(fun_eval)
{
/* like xget, except pronoun substitution is done */
dbref thing;
char tbuf1[BUFFER_LEN];
char *tbuf2;
thing = match_thing(privs, args[0]);
if (thing == NOTHING) {
strcpy(buff, "#-1 NO SUCH OBJECT VISIBLE");
return;
}
strcpy(tbuf1, do_get_attrib(privs, thing, args[1]));
tbuf2 = exec(thing, privs, 0, tbuf1);
strcpy(buff, tbuf2);
free(tbuf2);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
}
FUNCTION(fun_v)
{
/* handle 0-9, va-vz, n, l, # */
int c;
switch (c = args[0][0]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (!wptr[c - '0']) {
buff[0] = '\0';
return;
}
if(strlen(wptr[c - '0']) < BUFFER_LEN)
strcpy(buff, wptr[c - '0']);
else
buff[0] = '\0';
break;
case 'n':
case 'N':
if(args[0][1]) {
strcpy(buff, do_get_attrib(privs, privs, args[0]));
} else
strcpy(buff, Name(doer));
break;
case '#':
sprintf(buff, "#%d", doer);
break;
case 'l': case 'L':
if(args[0][1]) {
strcpy(buff, do_get_attrib(privs, privs, args[0]));
} else
/*
* giving the location does not violate security, since the object
* is the enactor.
*/
sprintf(buff, "#%d", Location(doer));
break;
/* objects # */
case '!':
sprintf(buff, "#%d", privs);
break;
default:
strcpy(buff, do_get_attrib(privs, privs, args[0]));
}
}
FUNCTION(fun_s)
{
char *s;
s = exec(privs, doer, 0, args[0]);
strcpy(buff, s);
free(s);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
}
static void do_userfn(player, cause, obj, attrib, args, buff, flag)
dbref player, cause, obj;
ATTR *attrib;
char *args[10];
char *buff;
int flag; /* 0 if ufun, 1 if gfun */
{
int a;
char *result;
char *tptr[10];
char tbuf1[BUFFER_LEN];
char *bp = tbuf1;
if (!attrib) {
strcpy(buff, "#-1 NO SUCH USER FUNCTION");
return;
}
if ((flag == 0) && !Can_Read_Attr(player, obj, attrib)) {
strcpy(buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
return;
}
safe_str(uncompress(attrib->value), tbuf1, &bp);
*bp = '\0';
/* save our stack */
for (a = 0; a < 10; a++)
tptr[a] = wptr[a];
/* copy the appropriate args into the stack */
if (flag == 0) {
for (a = 1; a < 10; a++)
wptr[a - 1] = args[a];
wptr[9] = NULL; /* sorry, can't have more than 9 args */
} else {
for (a = 0; a < 10; a++)
wptr[a] = args[a];
}
#ifdef NEVER
/* now find the value of the function with the new stack. We pass
* player instead of cause so that we have a way of checking to see
* if the calling object has a right to any information that may be
* retrieved. Therefore, if anything below the top-level U() needs
* the enactor, it must pass it as a parameter.
*/
result = exec(obj, player, 0, tbuf1);
#else
result = exec(obj, cause, 0, tbuf1);
#endif
/* restore the stack */
for (a = 0; a < 10; a++)
wptr[a] = tptr[a];
/* copy and free */
strcpy(buff, result);
free(result);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
}
FUNCTION(fun_ufun)
{
ATTR *attrib;
dbref obj;
/* find the user function attribute */
if (!args[0] || !*args[0]) {
strcpy(buff, "#-1 NOT FOUND");
return;
}
parse_attrib(privs, args[0], &obj, &attrib);
do_userfn(privs, doer, obj, attrib, args, buff, 0);
}
FUNCTION(fun_zfun)
{
int a;
char *tptr[10];
ATTR *attrib;
char tbuf1[BUFFER_LEN];
char *result;
dbref zone = Zone(privs);
if (zone == NOTHING) {
strcpy(buff, "#-1 INVALID ZONE");
return;
}
/* find the user function attribute */
attrib = atr_get(zone, upcasestr(args[0]));
if (!attrib) {
strcpy(buff, "#-1 NO SUCH USER FUNCTION");
return;
}
if (!Can_Read_Attr(privs, zone, attrib)) {
strcpy(buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
return;
}
if (strlen(uncompress(attrib->value)) < BUFFER_LEN)
strcpy(tbuf1, uncompress(attrib->value));
else {
strcpy(buff, "#-1 USER FUNCTION TOO LONG");
return;
}
/* save our stack */
for (a = 0; a < 10; a++)
tptr[a] = wptr[a];
/* copy the appropriate args into the stack */
for (a = 1; a < 10; a++)
wptr[a - 1] = args[a];
wptr[9] = NULL; /* sorry, can't have more than 9 args */
/* now find the value of the function with the new stack */
result = exec(zone, privs, 0, tbuf1);
/* restore the stack */
for (a = 0; a < 10; a++)
wptr[a] = tptr[a];
/* copy and free */
strcpy(buff, result);
free(result);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
}
/* --------------------------------------------------------------------------
* Local registers: SETQ, R
*/
FUNCTION(fun_setq)
{
/* sets a variable into a local register */
int r = atoi(args[0]);
if ((r < 0) || (r > 9)) {
strcpy(buff, "#-1 REGISTER OUT OF RANGE");
return;
}
strcpy(rptr[r], args[1]);
*buff = '\0';
}
FUNCTION(fun_r)
{
/* returns a local register */
int r = atoi(args[0]);
if ((r < 0) || (r > 9)) {
strcpy(buff, "#-1 REGISTER OUT OF RANGE");
return;
}
strcpy(buff, rptr[r]);
}
/* --------------------------------------------------------------------------
* High-order functions: FILTER, FOLD
*/
FUNCTION(fun_fold)
{
/* iteratively evaluates an attribute with a list of arguments and
* optional base case. With no base case, the first list element is
* passed as %0, and the second as %1. The attribute is then evaluated
* with these args. The result is then used as %0, and the next arg as
* %1. Repeat until no elements are left in the list. The base case
* can provide a starting point.
*/
dbref thing;
ATTR *attrib;
char abuf[BUFFER_LEN], rstore[BUFFER_LEN];
char *asave, *result, *bp, *cp;
char *tptr[10];
int i;
/* check our argument count */
if (!ok_nargs("FOLD", nargs, 2, 3, buff))
return;
/* find our object and attribute */
parse_attrib(privs, args[0], &thing, &attrib);
if (!GoodObject(thing) || !attrib) {
strcpy(buff, "#-1 NOT FOUND");
return;
}
if (!Can_Read_Attr(privs, thing, attrib)) {
strcpy(buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
return;
}
asave = safe_uncompress(attrib->value);
cp = args[1];
bp = buff;
strcpy(abuf, asave); /* save attribute text */
/* save our stack */
for (i = 0; i < 10; i++)
tptr[i] = wptr[i];
/* handle the first case */
if (nargs == 3) {
wptr[0] = args[2];
wptr[1] = parse_to(&cp, ' ', 0);
} else {
wptr[0] = parse_to(&cp, ' ', 0);
wptr[1] = parse_to(&cp, ' ', 0);
}
result = exec(privs, doer, EV_STRIP | EV_FCHECK | EV_EVAL, abuf);
strcpy(rstore, result);
free(result);
/* handle the rest of the cases */
while (cp && *cp) {
while (*cp == ' ') /* do not clobber tabs and newlines */
cp++;
if (*cp) {
wptr[0] = rstore;
wptr[1] = parse_to(&cp, ' ', 0);
strcpy(abuf, asave);
result = exec(privs, doer, EV_STRIP | EV_FCHECK | EV_EVAL, abuf);
strcpy(rstore, result);
free(result);
}
}
safe_str(rstore, buff, &bp);
*bp = '\0';
free(asave);
for (i = 0; i < 10; i++)
wptr[i] = tptr[i];
}
FUNCTION(fun_filter)
{
/* take a user-def function and a list, and return only those elements
* of the list for which the function evaluates to 1.
*/
dbref thing;
ATTR *attrib;
char *result, *bp, *cp, *asave;
char *tptr[10];
char abuf[BUFFER_LEN];
int i;
/* find our object and attribute */
parse_attrib(privs, args[0], &thing, &attrib);
if (!GoodObject(thing) || !attrib) {
strcpy(buff, "#-1 NOT FOUND");
return;
}
if (!Can_Read_Attr(privs, thing, attrib)) {
strcpy(buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
return;
}
asave = safe_uncompress(attrib->value);
for (i = 0; i < 10; i++)
tptr[i] = wptr[i];
cp = args[1];
bp = buff;
while (cp && *cp) {
while (*cp == ' ')
cp++;
if (*cp) {
wptr[0] = parse_to(&cp, ' ', 0);
strcpy(abuf, asave);
result = exec(privs, doer, EV_STRIP | EV_FCHECK | EV_EVAL, abuf);
if (*result == '1') {
if (bp != buff)
safe_chr(' ', buff, &bp);
safe_str(wptr[0], buff, &bp);
}
free(result);
}
}
*bp = '\0';
free(asave);
for (i = 0; i < 10; i++)
wptr[i] = tptr[i];
}
/* --------------------------------------------------------------------------
* Number-related functions: LNUM, ISNUM.
*/
FUNCTION(fun_isnum)
{
/* trivial little function, returns 0 or 1 depending on whether
* or not the string is a number. No overflow checks needed.
*/
sprintf(buff, "%d", is_number(args[0]));
}
FUNCTION(fun_lnum)
{
char *bp;
char tbuf1[8];
int i, x;
int done = 0;
x = atoi(args[0]);
if (x < 0) {
strcpy(buff, "#-1 NUMBER OUT OF RANGE");
return;
}
bp = buff;
safe_chr('0', buff, &bp);
for (i = 1; i < x && !done; i++) {
sprintf(tbuf1, " %d", i);
done = safe_str(tbuf1, buff, &bp);
}
*bp = '\0';
}
/* --------------------------------------------------------------------------
* Arithmetic on both integers and floating points: ADD, SUB, MUL, GT, GTE,
* LT, LTE, EQ, NEQ, MAX, MIN, SIGN.
*/
FUNCTION(fun_add)
{
if (is_number(args[0]) && is_number(args[1]))
fval(buff, aton(args[0]) + aton(args[1]));
else
strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
}
FUNCTION(fun_sub)
{
if (is_number(args[0]) && is_number(args[1]))
fval(buff, aton(args[0]) - aton(args[1]));
else
strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
}
FUNCTION(fun_mul)
{
if (is_number(args[0]) && is_number(args[1]))
fval(buff, aton(args[0]) * aton(args[1]));
else
strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
}
FUNCTION(fun_gt)
{
sprintf(buff, "%d", (aton(args[0]) > aton(args[1])));
}
FUNCTION(fun_gte)
{
sprintf(buff, "%d", (aton(args[0]) >= aton(args[1])));
}
FUNCTION(fun_lt)
{
sprintf(buff, "%d", (aton(args[0]) < aton(args[1])));
}
FUNCTION(fun_lte)
{
sprintf(buff, "%d", (aton(args[0]) <= aton(args[1])));
}
FUNCTION(fun_eq)
{
sprintf(buff, "%d", (aton(args[0]) == aton(args[1])));
}
FUNCTION(fun_neq)
{
sprintf(buff, "%d", (aton(args[0]) != aton(args[1])));
}
FUNCTION(fun_max)
{
double max;
int i = 1;
if (!args[0]) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
return;
} else
max = aton(args[0]);
while (i < 10 && args[i]) {
max = (aton(args[i]) > max) ? aton(args[i]) : max;
i++;
}
fval(buff, max);
}
FUNCTION(fun_min)
{
double min;
int i = 1;
if (!args[0]) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
return;
} else
min = aton(args[0]);
while (i < 10 && args[i]) {
min = (aton(args[i]) < min) ? aton(args[i]) : min;
i++;
}
fval(buff, min);
}
FUNCTION(fun_sign)
{
int x = aton(args[0]);
if (x == 0)
strcpy(buff, "0");
else if (x > 0)
strcpy(buff, "1");
else
strcpy(buff, "-1");
}
/* --------------------------------------------------------------------------
* Integer-only functions: TRUNC, DIV, MOD, ABS, DIST2D, DIST3D.
*/
FUNCTION(fun_trunc)
{
sprintf(buff, "%d", atoi(args[0]));
}
FUNCTION(fun_div)
{
int bot;
if (is_number(args[0]) && is_number(args[1])) {
bot = atoi(args[1]);
if (bot == 0) {
strcpy(buff, "#-1 DIVISION BY ZERO");
} else {
sprintf(buff, "%d", atoi(args[0]) / bot);
}
} else {
strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
}
}
FUNCTION(fun_mod)
{
int bot;
if (is_number(args[0]) && is_number(args[1])) {
bot = atoi(args[1]);
if (bot == 0) {
strcpy(buff, "#-1 DIVISION BY ZERO");
} else {
sprintf(buff, "%d", atoi(args[0]) % bot);
}
} else {
strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
}
}
FUNCTION(fun_abs)
{
sprintf(buff, "%d", abs(atoi(args[0])));
}
/* this function and dist3d are taken from the 2.0 code */
FUNCTION(fun_dist2d)
{
int d;
double r;
d = atoi(args[0]) - atoi(args[2]);
r = (double) (d * d);
d = atoi(args[1]) - atoi(args[3]);
r += (double) (d * d);
d = (int) (sqrt(r) + 0.5);
sprintf(buff, "%d", d);
}
FUNCTION(fun_dist3d)
{
int d;
double r;
d = atoi(args[0]) - atoi(args[3]);
r = (double) (d * d);
d = atoi(args[1]) - atoi(args[4]);
r += (double) (d * d);
d = atoi(args[2]) - atoi(args[5]);
r += (double) (d * d);
d = (int) (sqrt(r) + 0.5);
sprintf(buff, "%d", d);
}
/* --------------------------------------------------------------------------
* Floating-point-only functions: FDIV, FLOOR, CEIL, ROUND, PI, E,
* SIN, ASIN, COS, ACOS, TAN, ATAN, EXP, POWER, LN, LOG
*/
#ifdef FLOATING_POINTS
FUNCTION(fun_fdiv)
{
double bot;
if (is_number(args[0]) && is_number(args[1])) {
bot = atof(args[1]);
if (bot == 0) {
strcpy(buff, "#-1 DIVISION BY ZERO");
} else {
fval(buff, atof(args[0]) / bot);
}
} else {
strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
}
}
FUNCTION(fun_floor)
{
sprintf(buff, "%.0f", floor(atof(args[0])));
}
FUNCTION(fun_ceil)
{
sprintf(buff, "%.0f", ceil(atof(args[0])));
}
FUNCTION(fun_round)
{
const char *fstr;
switch (atoi(args[1])) {
case 1: fstr = "%.1f"; break;
case 2: fstr = "%.2f"; break;
case 3: fstr = "%.3f"; break;
case 4: fstr = "%.4f"; break;
case 5: fstr = "%.5f"; break;
case 6: fstr = "%.6f"; break;
default: fstr = "%.0f"; break;
}
sprintf(buff, fstr, atof(args[0]));
/* Handle the bizarre "-0" sprintf problem. */
if (!strcmp(buff, "-0")) {
strcpy(buff, "0");
}
}
FUNCTION(fun_pi)
{
strcpy(buff, "3.141592");
}
FUNCTION(fun_e)
{
strcpy(buff, "2.718281");
}
FUNCTION(fun_sin)
{
fval(buff, sin(atof(args[0])));
}
FUNCTION(fun_asin)
{
double n = atof(args[0]);
if ((n < -1) || (n > 1))
strcpy(buff, "#-1 OUT OF RANGE");
else
fval(buff, asin(n));
}
FUNCTION(fun_cos)
{
fval(buff, cos(atof(args[0])));
}
FUNCTION(fun_acos)
{
double n = atof(args[0]);
if ((n < -1) || (n > 1))
strcpy(buff, "#-1 OUT OF RANGE");
else
fval(buff, acos(n));
}
FUNCTION(fun_tan)
{
fval(buff, tan(atof(args[0])));
}
FUNCTION(fun_atan)
{
fval(buff, atan(atof(args[0])));
}
FUNCTION(fun_exp)
{
fval(buff, exp(atof(args[0])));
}
FUNCTION(fun_power)
{
double n;
if ((n = atof(args[0])) < 0)
strcpy(buff, "#-1 POWER OF NEGATIVE");
else
fval(buff, pow(n, atof(args[1])));
}
FUNCTION(fun_ln)
{
double n;
if ((n = atof(args[0])) > 0)
fval(buff, log(n));
else
strcpy(buff, "#-1 OUT OF RANGE");
}
FUNCTION(fun_log)
{
double n;
if ((n = atof(args[0])) > 0)
fval(buff, log10(n));
else
strcpy(buff, "#-1 OUT OF RANGE");
}
#endif /* FLOATING_POINTS */
/* --------------------------------------------------------------------------
* String functions: FIRST, REST, STRLEN, COMP, POS, MID, EXTRACT, WORDPOS,
* MATCH, CAT, REMOVE, MEMBER, FLIP, UCSTR, LCSTR, WORDS, BEFORE, AFTER,
* STRCAT, REVWORDS, MERGE, SPLICE, REPEAT, GREP, LJUST, RJUST
*/
FUNCTION(fun_first)
{
/* read first word from a string */
char *s, *b;
b = skip_space(args[0]);
s = seek_char(b, ' ');
if (s)
*s = '\0';
if(strlen(b) < BUFFER_LEN)
strcpy(buff, b);
else
buff[0] = '\0';
}
FUNCTION(fun_rest)
{
char *s;
/* skip leading space */
s = skip_space(args[0]);
/* skip first word */
s = seek_char(s, ' ');
/* skip leading space */
s = skip_space(s);
if(strlen(s) < BUFFER_LEN)
strcpy(buff, s);
else
buff[0] = '\0';
}
FUNCTION(fun_strlen)
{
sprintf(buff, "%d", strlen(args[0]));
}
FUNCTION(fun_mid)
{
int l = atoi(args[1]), len = atoi(args[2]);
if ((l < 0) || (len < 0) || ((len + l) > BUFFER_LEN)) {
strcpy(buff, "#-1 OUT OF RANGE");
return;
}
if (l < strlen(args[0]))
strcpy(buff, args[0] + l);
else
*buff = 0;
buff[len] = 0;
}
FUNCTION(fun_comp)
{
int x;
x = strcmp(args[0], args[1]);
if (x > 0)
strcpy(buff, "1");
else if (x < 0)
strcpy(buff, "-1");
else
strcpy(buff, "0");
}
FUNCTION(fun_pos)
{
int i = 1;
char *t, *u, *s = args[1];
while (*s) {
u = s;
t = args[0];
while (*t && *t == *u)
++t, ++u;
if (*t == '\0') {
sprintf(buff, "%d", i);
return;
}
++i, ++s;
}
strcpy(buff, "#-1");
return;
}
FUNCTION(fun_match)
{
/* compares two strings with possible wildcards, returns the
* word position of the match. Based on the 2.0 version of this
* function.
*/
int wcount = 1;
char *s = args[0];
char *r;
do {
r = skip_space(s);
s = seek_char(r, ' ');
if (*s)
*s++ = '\0';
if (local_wild_match(args[1], r)) {
/* found it */
sprintf(buff, "%d", wcount);
return;
}
wcount++;
} while (*s);
strcpy(buff, "0"); /* no match */
}
FUNCTION(fun_strmatch)
{
/* matches a wildcard pattern for an _entire_ string */
if (local_wild_match(args[1], args[0]))
strcpy(buff, "1");
else
strcpy(buff, "0");
}
/* taken from the 2.0 code */
FUNCTION(fun_wordpos)
{
char *cp;
char done, inspace;
int charpos = atoi(args[1]);
int word = 1;
for (inspace = 0, done = 0, cp = args[0]; cp && *cp && !done; cp++) {
if ((*cp == ' ') && (!inspace)) {
word++;
inspace = 1;
}
if ((*cp != ' ') && (inspace))
inspace = 0;
if ((cp - args[0] + 1) == charpos)
done = 1;
}
if (!done)
strcpy(buff, "#-1");
else
sprintf(buff, "%d", word);
}
FUNCTION(fun_extract)
{
int start = atoi(args[1]), len = atoi(args[2]);
char *s = args[0], *r;
if ((start < 1) || (len < 1)) {
*buff = 0;
return;
}
start--;
while (start && *s) {
while (*s && (*s == ' '))
s++;
while (*s && (*s != ' '))
s++;
start--;
}
while (*s && (*s == ' '))
s++;
r = s;
while (len && *s) {
while (*s && (*s == ' '))
s++;
while (*s && (*s != ' '))
s++;
len--;
}
*s = 0;
if(strlen(r) < BUFFER_LEN)
strcpy(buff, r);
else
buff[0] = '\0';
}
int translate(arg)
char *arg;
{
int temp;
char *temp2;
if (arg[0] == '#')
if ((temp = atoi(arg+1)) == -1)
return 0;
else
return temp;
else
temp2 = arg;
while (isspace(*temp2))
temp2++;
if ((*temp2 == '\0') && (temp2 == arg))
return 0;
if (isdigit(*temp2))
return atoi(temp2);
return 1;
}
FUNCTION(fun_cat)
{
char *bp;
int i;
bp = buff;
safe_str(args[0], buff, &bp);
for (i = 1; i < nargs; i++) {
safe_chr(' ', buff, &bp);
safe_str(args[i], buff, &bp);
}
*bp = '\0';
}
FUNCTION(fun_strcat)
{
char *bp;
bp = buff;
safe_str(args[0], buff, &bp);
safe_str(args[1], buff, &bp);
*bp = '\0';
}
FUNCTION(fun_remove)
{
char *s, *t, *p;
/*
* This function is 'safe' since it is impossible that any of the arg[]
* elements will be larger than BUFFER_LEN, and in the worst case, this
* does nothing to the the length, so still < BUFFER_LEN
*/
if (index(args[1],' ')) {
strcpy(buff, "#-1 CAN ONLY DELETE ONE ELEMENT");
return;
}
/* Jump to each space in the list (except for the first word).
* Move over the word. If it doesn't match, jump to the next space
* and try again. We have to compare until the end of the word we
* want to match, and that should put us at the end of the word
* in the list. Otherwise, we go seeking again. If we don't find
* anything, we just copy the original string.
*/
s = p = args[0];
while (s) {
t = args[1];
if (*p != *t) {
if ((s = (char *)index(p, ' ')) != NULL)
p = s + 1;
continue;
}
while (*p && *t && (*p == *t)) {
p++;
t++;
}
if ((*t == '\0') && ((*p == ' ') || (*p == '\0'))) {
if (*p == '\0') { /* remove the last element */
*s = '\0';
strcpy(buff, args[0]);
} else if (s == args[0]) { /* remove the first element */
p++;
strcpy(buff, p);
} else { /* remove a middle element */
*s = '\0';
sprintf(buff, "%s%s", args[0], p);
}
return;
}
if ((s = (char *)index(p, ' ')) != NULL)
p = s + 1;
}
strcpy(buff, args[0]);
}
FUNCTION(fun_member)
{
char *s, *t;
int el;
if (index(args[1],' ')) {
strcpy(buff,"#-1 CAN ONLY TEST ONE ELEMENT");
return;
}
s = args[0];
el = 1;
do {
t = skip_space(s);
s = seek_char(t, ' ');
if (*s)
*s++ = '\0';
if (!strcmp(args[1], t)) {
sprintf(buff, "%d", el);
return;
}
el++;
} while (*s);
strcpy(buff, "0"); /* not found */
}
FUNCTION(fun_before)
{
int n = strlen(args[1]);
char *s = args[0];
char *word = args[1];
char *p;
if (!*word) {
/* second argument is a space. Act like "first" function */
p = (char *) index(s, ' ');
if (p != NULL)
*p = '\0'; /* cut off after first word */
strcpy(buff, s);
return;
}
while (*s) {
/* find the first character in the string that matches the first
* character of the target word
*/
p = (char *) index(s, *word);
if (p == NULL) {
/* target string not found, return original string */
strcpy(buff, args[0]);
return;
}
/* check to see if the rest of the word matches */
if (!strncmp(p, word, n)) {
/* exact match. Don't need to worry about overflowing buffers,
* so just truncate and return string.
*/
*p = '\0';
strcpy(buff, args[0]);
return;
}
/* rest of the word didn't match, keep looking */
s = p + 1;
}
/* didn't find anything, just return original string */
strcpy(buff, args[0]);
}
FUNCTION(fun_after)
{
int n = strlen(args[1]);
char *s = args[0];
char *word = args[1];
char *p;
if (!*word) {
/* second argument is a space. Act like "rest" function */
p = (char *) index(s, ' ');
if (p == NULL)
*buff = '\0';
else
strcpy(buff, p + 1);
return;
}
while (*s) {
/* find the first character in the string that matches the first
* character of the target word
*/
p = (char *) index(s, *word);
if (p == NULL) {
/* target string not found, return nothing */
*buff = '\0';
return;
}
/* check to see if the rest of the word matches */
if (!strncmp(p, word, n)) {
/* exact match, copy and return. No need to check to see if we
* overflowed the buffer, since the string will be shorter.
*/
s = p + n;
strcpy(buff, s);
return;
}
/* rest of the word didn't match, keep looking */
s = p + 1;
}
/* didn't find anything */
*buff = '\0';
}
void do_flip(s, r)
char *s;
char *r;
/* utility function to reverse a string */
{
char *p;
p = strlen(s) + r;
*p-- = '\0';
while (*s)
*p-- = *s++;
}
FUNCTION(fun_flip)
{
do_flip(args[0], buff);
}
FUNCTION(fun_revwords)
{
/* based on the 2.0 code */
char tbuf1[BUFFER_LEN];
char *bp, *tp, *lp;
char c;
/* reverse the string */
do_flip(args[0], tbuf1);
/* reverse each word in the string. The words themselves will then be
* forwards again, with their order reversed (from the prior flip).
*/
tp = tbuf1;
bp = buff;
while (*tp) {
lp = tp;
while (!isspace(*tp)) /* skip spaces */
tp++;
if (tp != lp) {
c = *tp;
*tp = '\0'; /* split off a word */
do_flip(lp, bp); /* reverse word */
bp += tp - lp; /* move pointer past stuff we've added */
*tp = c; /* put back the original char */
}
if (!*tp)
break;
*bp++ = *tp++;
}
*bp = '\0'; /* terminate */
}
static int do_wordcount(str)
char *str;
{
/* count the number of words in a string */
char *s = str;
int count = 0;
while (*s && (*s == ' '))
s++;
while (*s) {
count++;
while (*s && (*s != ' '))
s++;
while (*s && (*s == ' '))
s++;
}
return (count);
}
FUNCTION(fun_words)
{
sprintf(buff, "%d", do_wordcount(args[0]));
}
FUNCTION(fun_merge)
{
/* given s1, s2, and a char, for each character in s1, if the char
* is the same as the given char, replace it with the corresponding
* char in s2.
*/
char *str, *rep, *bp;
char c;
/* do length checks first */
if (strlen(args[0]) != strlen(args[1])) {
strcpy(buff, "#-1 STRING LENGTHS MUST BE EQUAL");
return;
}
if (strlen(args[2]) > 1) {
strcpy(buff, "#-1 TOO MANY CHARACTERS");
return;
}
/* find the character to look for */
if (!*args[2])
c = ' ';
else
c = *args[2];
/* walk strings, copy from the appropriate string */
for (str = args[0], rep = args[1], bp = buff;
*str && *rep;
str++, rep++, bp++) {
if (*str == c)
*bp = *rep;
else
*bp = *str;
}
*bp = '\0'; /* terminate */
/* there is no error checking necessary since everything passed in
* will be smaller than BUFFER_LEN, and the string cannot be any
* larger, since we're just doing a copy.
*/
}
FUNCTION(fun_splice)
{
/* like MERGE(), but does it for a word */
char *bp;
char *p1, *p2;
char *q1, *q2;
int words;
int i;
/* length checks */
if (!*args[2]) {
strcpy(buff, "#-1 NEED A WORD");
return;
}
if (do_wordcount(args[2]) != 1) {
strcpy(buff, "#-1 TOO MANY WORDS");
return;
}
words = do_wordcount(args[0]);
if (words != do_wordcount(args[1])) {
strcpy(buff, "#-1 NUMBER OF WORDS MUST BE EQUAL");
return;
}
/* loop through the two lists */
for (bp = buff, i = 0, p1 = args[0], q1 = args[1];
i < words;
i++, p1 = p2, q1 = q2) {
if ((p2 = (char *) index(p1, ' ')) != NULL)
*p2++ = '\0';
if ((q2 = (char *) index(q1, ' ')) != NULL)
*q2++ = '\0';
if (bp != buff)
safe_chr(' ', buff, &bp);
if (!strcmp(p1, args[2]))
safe_str(q1, buff, &bp); /* replace */
else
safe_str(p1, buff, &bp); /* copy */
}
*bp = '\0';
}
FUNCTION(fun_lcstr)
{
char *ap;
ap = args[0];
*buff = '\0';
while (*ap) {
if (isupper(*ap))
*buff++ = tolower(*ap++);
else
*buff++ = *ap++;
}
*buff++ = '\0';
/* No need to check buffer length */
}
FUNCTION(fun_ucstr)
{
char *ap;
ap = args[0];
*buff = '\0';
while (*ap) {
if (islower(*ap))
*buff++ = toupper(*ap++);
else
*buff++ = *ap++;
}
*buff++ = '\0';
/* No need to check buffer length */
}
FUNCTION(fun_repeat)
{
int times, i;
char *bp;
times = atoi(args[1]);
if (times < 1) {
*buff = '\0';
return;
}
if (times == 1) {
strcpy(buff, args[0]);
return;
}
if (strlen(args[0]) * times >= BUFFER_LEN) {
strcpy(buff, "#-1 STRING TOO LONG");
return;
}
bp = buff;
for (i = 0; i < times; i++)
safe_str(args[0], buff, &bp);
*bp = '\0';
}
FUNCTION(fun_scramble)
{
int n, i, j;
char c;
if (!args[0] || !*args[0]) {
*buff = '\0';
return;
}
strcpy(buff, args[0]);
n = strlen(buff); /* just the length of the string */
for (i = 0; i < n; i++) {
j = getrandom(n - i) + i;
c = buff[i];
buff[i] = buff[j];
buff[j] = c;
}
}
FUNCTION(fun_grep)
{
char *sptr[10];
int i;
char *tp;
dbref it = match_thing(privs, args[0]);
if (it == NOTHING) {
strcpy(buff, "#-1 NO SUCH OBJECT VISIBLE");
return;
}
/* make sure there's an attribute and a pattern */
if (!args[1] || !*args[1]) {
strcpy(buff, "#-1 NO SUCH ATTRIBUTE");
return;
}
if (!args[2] || !*args[2]) {
strcpy(buff, "#-1 INVALID GREP PATTERN");
return;
}
/* save wptr */
for (i = 0; i < 10; i++)
sptr[i] = wptr[i];
tp = grep_util(it, args[1], args[2], strlen(args[2]));
strcpy(buff, tp);
free(tp);
/* restore wptr */
for (i = 0; i < 10; i++)
wptr[i] = sptr[i];
}
FUNCTION(fun_ljust)
{
/* pads a string with trailing blanks (or other fill character) */
int spaces, i;
char c;
char *bp;
if (!ok_nargs("LJUST", nargs, 2, 3, buff))
return;
spaces = atoi(args[1]) - strlen(args[0]);
if (nargs == 3)
c = *args[2];
else
c = ' ';
if (spaces <= 0) {
/* no padding needed, just return string */
strcpy(buff, args[0]);
return;
}
if (spaces > BUFFER_LEN)
spaces = BUFFER_LEN;
bp = buff;
safe_str(args[0], buff, &bp);
for (i = 0; i < spaces; i++)
safe_chr(c, buff, &bp);
*bp = '\0';
}
FUNCTION(fun_rjust)
{
/* pads a string with leading blanks */
int spaces, i;
char c;
char *bp;
if (!ok_nargs("RJUST", nargs, 2, 3, buff))
return;
spaces = atoi(args[1]) - strlen(args[0]);
if (nargs == 3)
c = *args[2];
else
c = ' ';
if (spaces <= 0) {
/* no padding needed, just return string */
strcpy(buff, args[0]);
return;
}
if (spaces > BUFFER_LEN)
spaces = BUFFER_LEN;
bp = buff;
for (i = 0; i < spaces; i++)
safe_chr(c, buff, &bp);
safe_str(args[0], buff, &bp);
*bp = '\0';
}
/* --------------------------------------------------------------------------
* Functions involving lists with arbitrary separators: ITEMS, ELEMENT,
* INDEX, INSERT, REPLACE, DELETE
*/
FUNCTION(fun_items)
{
/* the equivalent of WORDS for an arbitrary separator */
char *s = args[0];
char c = *args[1];
int count = 0;
if (c == '\0')
c = ' ';
while (*s) {
count++;
do {
s++;
} while (*s && (*s != c));
}
sprintf(buff, "%d", count);
}
FUNCTION(fun_element)
{
/* the equivalent of MEMBER for an arbitrary separator */
char *s, *t;
char c;
int el;
c = *args[2];
if (c != '\0') {
if (index(args[1], c)) {
strcpy(buff, "#-1 CAN ONLY TEST ONE ELEMENT");
return;
}
} else {
c = ' ';
}
s = args[0];
el = 1;
do {
t = s;
s = seek_char(t, c);
if (*s)
*s++ = '\0';
if (local_wild_match(args[1], t)) {
sprintf(buff, "%d", el);
return;
}
el++;
} while (*s);
strcpy(buff, "0"); /* no match */
}
FUNCTION(fun_index)
{
/* more or less the equivalent of EXTRACT for an arbitrary separator */
int start, end;
char c;
char *s, *p, *bp;
s = args[0];
c = *args[1];
if (!c)
c = ' ';
start = atoi(args[2]);
end = atoi(args[3]);
*buff = '\0';
bp = buff;
if ((start < 1) || (end < 1) || (*s == '\0'))
return;
/* move s to the start of the item we want */
start--;
while (start && s && *s) {
if ((s = (char *) index(s, c)) != NULL) s++;
start--;
}
/* skip just spaces, not tabs or newlines, since people may MUSHcode things
* like "%r%tPolgara %r%tDurnik %r%tJavelin"
*/
while (s && *s && (*s == ' ')) s++;
if (!s || !*s) return;
/* now figure out where to end the string */
p = s;
while (end && p && *p) {
if ((p = (char *) index(p, c)) != NULL) {
if (--end == 0) {
/* trim trailing spaces (just true spaces) */
do {
p--;
} while (*p == ' ');
*(++p) = '\0';
safe_str(s, buff, &bp);
*bp = '\0';
return;
} else
p++;
}
}
/* if we've hit this point, we've run off the end of the string */
safe_str(s, buff, &bp);
*bp = '\0';
}
static void do_itemfuns(buff, str, num, word, sep, flag)
char *buff; /* the return buffer */
char *str; /* the original string */
char *num; /* the element number */
char *word; /* word to insert or replace */
char *sep; /* the separator */
int flag; /* op -- 0 delete, 1 replace, 2 insert */
{
char c;
int el, count;
char *sptr, *eptr, *bp;
/* figure out the separator character */
if (sep && *sep)
c = *sep;
else
c = ' ';
el = atoi(num);
if (el < 1) { /* no such position */
strcpy(buff, str);
return;
}
sptr = str;
count = 1;
/* we can't remove anything before the first position */
if (el < 1) {
strcpy(buff, str);
return;
}
/* go to the correct item in the string */
while (sptr && (count < el)) {
sptr++;
sptr = (char *)index(sptr, c);
count++;
}
if (!sptr) {
/* we've run off the end of the string without finding anything */
strcpy(buff, str);
return;
}
/* now find the end of that element */
if (sptr != str)
*sptr++ = '\0';
eptr = (char *)index(sptr, c);
switch (flag) {
case 0:
/* deletion */
if (!eptr) { /* last element in the string */
strcpy(buff, str);
} else if (sptr == str) { /* first element in the string */
*eptr++ = '\0'; /* chop leading separator */
strcpy(buff, eptr);
} else {
sprintf(buff, "%s%s", str, eptr);
}
break;
case 1:
/* replacing */
bp = buff;
if (!eptr) { /* last element in string */
safe_str(str, buff, &bp);
safe_chr(c, buff, &bp);
safe_str(word, buff, &bp);
} else if (sptr == str) { /* first element in string */
safe_str(word, buff, &bp);
safe_str(eptr, buff, &bp);
} else {
safe_str(str, buff, &bp);
safe_chr(c, buff, &bp);
safe_str(word, buff, &bp);
safe_str(eptr, buff, &bp);
}
*bp = '\0';
break;
case 2:
/* insertion */
bp = buff;
if (sptr == str) { /* first element in string */
safe_str(word, buff, &bp);
safe_chr(c, buff, &bp);
safe_str(str, buff, &bp);
} else {
safe_str(str, buff, &bp);
safe_chr(c, buff, &bp);
safe_str(word, buff, &bp);
safe_chr(c, buff, &bp);
safe_str(sptr, buff, &bp);
}
*bp = '\0';
break;
}
}
FUNCTION(fun_delete)
{
/* delete a word at position X of a list */
if (!ok_nargs("DELETE", nargs, 2, 3, buff))
return;
do_itemfuns(buff, args[0], args[1], NULL, args[2], 0);
}
FUNCTION(fun_replace)
{
/* replace a word at position X of a list */
if (!ok_nargs("REPLACE", nargs, 3, 4, buff))
return;
do_itemfuns(buff, args[0], args[1], args[2], args[3], 1);
}
FUNCTION(fun_insert)
{
/* insert a word at position X of a list */
if (!ok_nargs("INSERT", nargs, 3, 4, buff))
return;
do_itemfuns(buff, args[0], args[1], args[2], args[3], 2);
}
/* --------------------------------------------------------------------------
* Word functions: CAPSTR, ART, SUBJ, OBJ, POSS, ALPHAMAX, ALPHAMIN, ISWORD
*/
FUNCTION(fun_isword)
{
/* is every character a letter? */
char *p;
for (p = args[0]; *p; p++) {
if (!isalpha(*p)) {
strcpy(buff, "0");
return;
}
}
strcpy(buff, "1");
}
FUNCTION(fun_capstr)
{
strcpy(buff, args[0]);
if (islower(*buff))
*buff = toupper(*buff);
/* No need to check buffer length */
}
FUNCTION(fun_art)
{
/* checks a word and returns the appropriate article, "a" or "an" */
char c = tolower(*args[0]);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
strcpy(buff, "an");
else
strcpy(buff, "a");
}
/* utility function to find sex of object, used by SUBJ, OBJ, and POSS.
* Returns 0 for none, 1 for female, 2 for male
*/
int find_sex(thing)
dbref thing;
{
ATTR *a;
int gender;
a = atr_get(thing, "SEX");
if (!a)
gender = 0;
else {
switch (*uncompress(a->value)) {
case 'M': case 'm':
gender = 2;
break;
case 'W': case 'w': case 'F': case 'f':
gender = 1;
break;
default:
gender = 0;
}
}
return gender;
}
FUNCTION(fun_subj)
{
dbref thing;
int gender;
thing = match_thing(privs, args[0]);
if (thing == NOTHING) {
strcpy(buff, "#-1 NO MATCH");
return;
}
gender = find_sex(thing);
switch (gender) {
case 1:
strcpy(buff, "she");
break;
case 2:
strcpy(buff, "he");
break;
default:
strcpy(buff, "it");
}
}
FUNCTION(fun_obj)
{
dbref thing;
int gender;
thing = match_thing(privs, args[0]);
if (thing == NOTHING) {
strcpy(buff, "#-1 NO MATCH");
return;
}
gender = find_sex(thing);
switch (gender) {
case 1:
strcpy(buff, "her");
break;
case 2:
strcpy(buff, "him");
break;
default:
strcpy(buff, "it");
}
}
FUNCTION(fun_poss)
{
dbref thing;
int gender;
thing = match_thing(privs, args[0]);
if (thing == NOTHING) {
strcpy(buff, "#-1 NO MATCH");
return;
}
gender = find_sex(thing);
switch (gender) {
case 1:
strcpy(buff, "her");
break;
case 2:
strcpy(buff, "his");
break;
default:
strcpy(buff, "its");
}
}
FUNCTION(fun_alphamax)
{
char *amax;
int i = 1;
if (!args[0]) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
return;
} else
amax = args[0];
while ((i < 10) && args[i]) {
amax = (strcmp(amax, args[i]) > 0) ? amax : args[i];
i++;
}
sprintf(buff, "%s", amax);
}
FUNCTION(fun_alphamin)
{
char *amin;
int i = 1;
if (!args[0]) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
return;
} else
amin = args[0];
while ((i < 10) && args[i]) {
amin = (strcmp(amin, args[i]) < 0) ? amin : args[i];
i++;
}
sprintf(buff, "%s", amin);
}
/* --------------------------------------------------------------------------
* MUSH utilities: FLAGS, NUM, RNUM, LEXITS, EXITS, LCON, CON, NEXT, MAIL,
* NEARBY, TYPE, HASFLAG, LOCK, ELOCK, LOC, HOME, OWNER, NAME, PMATCH
* LOCATE, ROOM, WHERE, CONTROLS, VISIBLE
*/
FUNCTION(fun_flags)
{
dbref thing;
thing = match_thing(privs, args[0]);
if (thing == NOTHING) {
strcpy(buff,"#-1");
return;
}
strcpy(buff, unparse_flags(thing, privs));
}
FUNCTION(fun_num)
{
sprintf(buff, "#%d", match_thing(privs, args[0]));
}
#ifdef DO_GLOBALS
FUNCTION(fun_rnum)
{
dbref place = match_thing(privs, args[0]);
char *name = args[1];
dbref thing;
init_match_remote(place, name, NOTYPE);
match_remote();
switch (thing = match_result()) {
case NOTHING:
strcpy(buff, "#-1 NO MATCH");
break;
case AMBIGUOUS:
strcpy(buff, "#-1 AMBIGUOUS MATCH");
break;
default:
sprintf(buff, "#%d", thing);
}
}
#endif
/*
* fun_lcon, fun_lexits, fun_con, fun_exit, fun_next, and next_exit were all
* re-coded by d'mike, 7/12/91. next_con was added at this time as well.
*
* The function behavior was changed by Amberyl, to remove what she saw
* as a security problem, since mortals could get the contents of rooms
* they didn't control, thus (if they were willing to go through the trouble)
* they could build a scanner to locate anything they wanted.
*
* You can get the contents of any room you control, regardless of whether
* or not the object is dark. You can get the contents of your current
* location, _except_ for dark objects (and DARK/OPAQUE rooms). You CANNOT
* get the contents of anything else, regardless of whether or not you have
* objects in it. This latter behavior is exhibited by 2.0.
*
* The same behavior is true for exits, except OPAQUE doesn't apply.
*/
FUNCTION(fun_lcon)
{
dbref it = match_thing(privs, args[0]);
dbref thing;
char *bp;
int sees_loc;
bp = buff;
if (it != NOTHING) {
sees_loc = Can_Examine(privs, it) ? 1 : 0;
if (sees_loc ||
((Location(privs) == it) && !Dark(it) && !Opaque(it))) {
DOLIST(thing, Contents(it)) {
if (!Dark(thing) || sees_loc || controls(privs, thing)) {
if (bp != buff)
safe_chr(' ', buff, &bp);
safe_str(tprintf("#%d", thing), buff, &bp);
}
}
}
*bp = '\0';
} else
strcpy(buff, "#-1");
}
/* fun_con is a wrapper for next_con now. */
FUNCTION(fun_con)
{
dbref it = match_thing(privs, args[0]);
if (it != NOTHING)
sprintf(buff, "#%d", next_con(privs, Contents(it)));
else
strcpy(buff, "#-1");
}
/* return next contents that is ok to see */
dbref next_con(player, this)
dbref player;
dbref this;
{
dbref loc;
int sees_loc;
if ((this == NOTHING) || ((loc = Location(this)) == NOTHING))
return NOTHING;
sees_loc = Can_Examine(player, loc) ? 1 : 0;
if (sees_loc || ((Location(player) == loc) && !Dark(loc) && !Opaque(loc))) {
while ((this != NOTHING) && !sees_loc && !controls(player, this)
&& Dark(this))
this = Next(this);
}
return(this);
}
/* return next exit that is ok to see */
dbref next_exit(player, this)
dbref player;
dbref this;
{
dbref loc;
int sees_loc;
if ((this == NOTHING) || ((loc = Home(this)) == NOTHING))
return NOTHING;
sees_loc = Can_Examine(player, loc) ? 1 : 0;
if (sees_loc || ((Location(player) == loc) && !Dark(loc))) {
while ((this != NOTHING) && !sees_loc && !controls(player, this)
&& Dark(this))
this = Next(this);
}
return(this);
}
FUNCTION(fun_lexits)
{
dbref it = match_thing(privs, args[0]);
dbref thing;
char *bp;
int sees_loc;
bp = buff;
sees_loc = Can_Examine(privs, it) ? 1 : 0;
if (it != NOTHING) {
if (sees_loc || ((Location(privs) == it) && !Dark(it))) {
DOLIST(thing, Exits(it)) {
if (!Dark(thing) || sees_loc || controls(privs, thing)) {
if (bp != buff)
safe_chr(' ', buff, &bp);
safe_str(tprintf("#%d", thing), buff, &bp);
}
}
}
*bp = '\0';
} else
strcpy(buff,"#-1");
}
/* fun_exit is really just a wrapper for next_exit now... */
FUNCTION(fun_exit)
{
dbref it = match_thing(privs, args[0]);
if (it != NOTHING)
sprintf(buff, "#%d", next_exit(privs, Exits(it)));
else
strcpy(buff, "#-1");
return;
}
FUNCTION(fun_next)
{
dbref it = match_thing(privs, args[0]);
if (it != NOTHING) {
if (Typeof(it) != TYPE_EXIT) {
sprintf(buff, "#%d", next_con(privs, Next(it)));
return;
} else {
sprintf(buff, "#%d", next_exit(privs, Next(it)));
return;
}
}
strcpy(buff, "#-1");
return;
}
FUNCTION(fun_nearby)
{
dbref obj1 = match_thing(privs, args[0]);
dbref obj2 = match_thing(privs, args[1]);
if (!controls(privs, obj1) && !controls(privs, obj2)
&& !nearby(privs, obj1) && !nearby(privs, obj2)) {
strcpy(buff, "#-1 NO OBJECTS CONTROLLED");
return;
}
if (!GoodObject(obj1) || !GoodObject(obj2)) {
strcpy(buff, "#-1");
return;
} else
sprintf(buff, "%d", nearby(obj1, obj2));
}
FUNCTION(fun_controls)
{
dbref it = match_thing(privs, args[0]);
dbref thing = match_thing(privs, args[1]);
sprintf(buff, "%d", controls(it, thing));
}
FUNCTION(fun_visible)
{
/* check to see if we have an object-attribute pair. If we don't,
* then we want to know about the whole object; otherwise, we're
* just interested in a single attribute.
* If we encounter an error, we return 0 rather than an error
* code, since if it doesn't exist, it obviously can't see
* anything or be seen.
*/
dbref it, thing;
char *name;
ATTR *a;
it = match_thing(privs, args[0]);
if (it == NOTHING) {
strcpy(buff, "0");
return;
}
if ((name = (char *) index(args[1], '/')) != NULL)
*name++ = '\0';
thing = match_thing(privs, args[1]);
if (name) {
a = atr_get(thing, upcasestr(name));
sprintf(buff, "%d", Can_Read_Attr(it, thing, a));
} else {
sprintf(buff, "%d", Can_Examine(it, thing));
}
}
FUNCTION(fun_type)
{
dbref it = match_thing(privs, args[0]);
if (it == NOTHING) {
strcpy(buff, "#-1");
return;
}
switch (Typeof(it)) {
case TYPE_PLAYER:
strcpy(buff, "PLAYER");
break;
case TYPE_THING:
strcpy(buff, "THING");
break;
case TYPE_EXIT:
strcpy(buff, "EXIT");
break;
case TYPE_ROOM:
strcpy(buff, "ROOM");
break;
default:
strcpy(buff, "WEIRD OBJECT");
fprintf(stderr, "WARNING: Weird object #%d (type %d)\n", it, Typeof(it));
}
}
FUNCTION(fun_hasflag)
{
int f = 0;
int toggle;
dbref it = match_thing(privs, args[0]);
char *p = args[1];
if (it == NOTHING) {
strcpy(buff, "#-1");
return;
}
f = find_flag(p, Typeof(it), &toggle, 0);
if (f == -1)
strcpy(buff, "0");
else if (toggle == 0)
strcpy(buff, (Flags(it) & f) ? "1" : "0");
else
strcpy(buff, ((Toggles(it) & f) &&
((f != PLAYER_SUSPECT) || See_All(privs))) ? "1" : "0");
}
static int handle_flaglists(player, name, fstr, type)
dbref player;
char *name;
char *fstr;
int type; /* 0 for orflags, 1 for andflags */
{
char *s;
object_flag_type flag;
int toggle, negate, temp;
int ret = type;
dbref it = match_thing(player, name);
toggle = negate = temp = 0;
if (it == NOTHING)
return 0;
for (s = fstr; *s; s++) {
/* Check for a negation sign. If we find it, we note it and
* increment the pointer to the next character.
*/
if (*s == '!') {
negate = 1;
s++;
}
if (!*s || ((flag = letter_to_flag(*s, Typeof(it), &toggle)) == -1)) {
/* Either we got a '!' that wasn't followed by a letter, or
* we couldn't find that flag. For AND, since we've failed
* a check, we can return false. Otherwise we just go on.
*/
if (type == 1)
return 0;
else
continue;
} else {
/* does the object have this flag? */
if ((!toggle && (Flags(it) & flag)) ||
(toggle && (Toggles(it) & flag)))
temp = 1;
else
temp = 0;
if ((type == 1) && ((negate && temp) || (!negate && !temp))) {
/* Too bad there's no NXOR function...
* At this point we've either got a flag and we don't want
* it, or we don't have a flag and we want it. Since it's
* AND, we return false.
*/
return 0;
} else if ((type == 0) &&
((!negate && temp) || (negate && !temp))) {
/* We've found something we want, in an OR. We OR a
* true with the current value.
*/
ret |= 1;
}
/* Otherwise, we don't need to do anything. */
}
}
return (ret);
}
FUNCTION(fun_orflags)
{
sprintf(buff, "%d", handle_flaglists(privs, args[0], args[1], 0));
}
FUNCTION(fun_andflags)
{
sprintf(buff, "%d", handle_flaglists(privs, args[0], args[1], 1));
}
static int get_locktype(obj, str)
dbref obj;
const char *str;
{
/* figure out a lock type */
if (!str || !*str || string_prefix("basic", str))
return BASICLOCK;
/* handle special cases */
switch (Typeof(obj)) {
case TYPE_ROOM:
if (string_prefix("tport", str))
return ENTERLOCK;
break;
case TYPE_PLAYER:
if (string_prefix("page", str))
return USELOCK;
break;
}
if (string_prefix("enter", str))
return ENTERLOCK;
else if (string_prefix("use", str))
return USELOCK;
return NOTHING; /* error */
}
FUNCTION(fun_lock)
{
dbref it;
const char *s;
char *p;
int ltype;
p = (char *) index(args[0], '/');
if (p)
*p++ = '\0';
it = match_thing(privs, args[0]);
ltype = get_locktype(it, p);
if ((it != NOTHING) && Can_Examine(privs, it) && (ltype != NOTHING)) {
switch (ltype) {
case BASICLOCK:
s = unparse_boolexp(privs, Key(it), 1);
break;
case ENTERLOCK:
s = unparse_boolexp(privs, Enterkey(it), 1);
break;
case USELOCK:
s = unparse_boolexp(privs, Usekey(it), 1);
break;
}
if (strlen(s) < BUFFER_LEN) {
sprintf(buff, "%s", s);
return;
}
}
strcpy(buff, "#-1");
return;
}
FUNCTION(fun_elock)
{
char *p;
int ltype;
dbref it;
dbref victim = match_thing(privs, args[1]);
p = (char *) index(args[0], '/');
if (p)
*p++ = '\0';
it = match_thing(privs, args[0]);
ltype = get_locktype(it, p);
if ((it != NOTHING) && (ltype != NOTHING)) {
/*
* We do not check for control because this can be bypassed by using
* an indirect lock.
*/
switch (ltype) {
case BASICLOCK:
sprintf(buff, "%d", eval_boolexp(victim, Key(it), it, 0, ltype));
break;
case ENTERLOCK:
sprintf(buff, "%d", eval_boolexp(victim, Enterkey(it), it, 0, ltype));
break;
case USELOCK:
sprintf(buff, "%d", eval_boolexp(victim, Usekey(it), it, 0, ltype));
}
return;
}
/* error */
strcpy(buff, "#-1");
return;
/* No length checking in this function necessary */
}
FUNCTION(fun_loc)
{
dbref it = match_thing(privs, args[0]);
if ((it != NOTHING) && Can_Locate(privs, it)) {
sprintf(buff, "#%d", Location(it));
return;
}
strcpy(buff, "#-1");
return;
}
FUNCTION(fun_where)
{
/* finds the "real" location of an object */
dbref it = match_thing(privs, args[0]);
if ((it != NOTHING) && Can_Locate(privs, it)) {
sprintf(buff, "#%d", where_is(it));
return;
}
strcpy(buff, "#-1");
return;
}
FUNCTION(fun_room)
{
dbref room;
int rec = 0;
dbref it = match_thing(privs, args[0]);
if ((it != NOTHING) && Can_Locate(privs, it)) {
room = Location(it);
if (!GoodObject(room)) {
strcpy(buff, "#-1");
return;
}
while ((Typeof(room) != TYPE_ROOM) && (rec < 15)) {
room = Location(room);
rec++;
}
if (rec > 15) {
notify(privs, "Too many containers.");
strcpy(buff, "#-1");
return;
} else {
sprintf(buff, "#%d", room);
}
} else {
notify(privs, "Permission denied.");
strcpy(buff, "#-1");
}
}
FUNCTION(fun_zone)
{
dbref it = match_thing(privs, args[0]);
if (it == NOTHING || !Can_Examine(privs, it)) {
strcpy(buff, "#-1");
return;
}
sprintf(buff, "#%d", Zone(it));
}
FUNCTION(fun_parent)
{
dbref it = match_thing(privs, args[0]);
if ((it == NOTHING) || !Can_Examine(privs, it)) {
strcpy(buff, "#-1");
return;
}
sprintf(buff, "#%d", Parent(it));
}
FUNCTION(fun_home)
{
dbref it = match_thing(privs, args[0]);
if ((it == NOTHING) || !Can_Examine(privs, it) ||
(Typeof(it) == TYPE_ROOM)) {
strcpy(buff,"#-1");
return;
}
sprintf(buff, "#%d", Exits(it));
}
FUNCTION(fun_money)
{
dbref it = match_thing(privs, args[0]);
if (it == NOTHING) {
strcpy(buff,"#-1");
return;
}
sprintf(buff,"%d", Pennies(it));
}
FUNCTION(fun_owner)
{
dbref it = match_thing(privs, args[0]);
if (it != NOTHING)
it = Owner(it);
sprintf(buff, "#%d", it);
}
FUNCTION(fun_name)
{
dbref it = match_thing(privs, args[0]);
if (it == NOTHING)
*buff = '\0';
else if (Typeof(it) == TYPE_EXIT) {
char *s;
if(strlen(Name(it)) < BUFFER_LEN) {
strcpy(buff, Name(it));
for (s = buff; *s && (*s != ';'); s++) ;
*s = 0;
} else
buff[0] = '\0';
} else {
if(strlen(Name(it)) < BUFFER_LEN)
strcpy(buff, Name(it));
else
buff[0] = '\0';
}
}
FUNCTION(fun_fullname)
{
dbref it;
if ((it = match_thing(privs, args[0])) == NOTHING)
*buff = '\0';
else
strcpy(buff, Name(it));
}
FUNCTION(fun_pmatch)
{
dbref target = lookup_player(args[0]);
if (target == NOTHING)
target = short_page(args[0]);
switch (target) {
case NOTHING:
notify(privs, "No match.");
strcpy(buff, "#-1");
break;
case AMBIGUOUS:
notify(privs, "I'm not sure who you mean.");
strcpy(buff, "#-2");
break;
default:
sprintf(buff, "#%d", target);
}
}
FUNCTION(fun_locate)
{
dbref looker;
object_flag_type pref_type;
dbref item;
char *p;
int keys = 0;
int done = 0;
/* find out what we're matching in relation to */
looker = match_thing(privs, args[0]);
if (looker == NOTHING) {
notify(privs, "I don't see that here.");
strcpy(buff, "#-1");
return;
}
if (!See_All(privs) && !controls(privs, looker)) {
notify(privs, "Permission denied.");
strcpy(buff, "#-1");
return;
}
/* find out our preferred match type */
pref_type = NOTYPE;
for (p = args[2]; *p; p++) {
switch (*p) {
case 'N':
pref_type = NOTYPE;
break;
case 'E':
pref_type = TYPE_EXIT;
break;
case 'P':
pref_type = TYPE_PLAYER;
break;
case 'R':
pref_type = TYPE_ROOM;
break;
case 'T':
pref_type = TYPE_THING;
break;
case 'L':
keys = 1;
break;
}
}
if (keys)
init_match_check_keys(looker, args[1], pref_type);
else
init_match(looker, args[1], pref_type);
/* now figure out where what kinds of matches we want done */
for (p = args[2]; *p && !done; p++) {
switch (*p) {
case '*':
match_everything();
done = 1;
break;
case 'a':
match_absolute();
break;
case 'e':
match_exit();
break;
case 'h':
match_here();
break;
case 'i':
match_possession();
break;
case 'm':
match_me();
break;
case 'n':
match_neighbor();
break;
case 'p':
match_player();
break;
case 'N': case 'E' : case 'P' : case 'R' : case 'T' : case 'L':
/* these are from previous type switch check. ignore. */
break;
default:
notify(privs, tprintf("I don't understand switch '%c'.", *p));
break;
}
}
/* report the results */
item = match_result();
if (item == NOTHING)
notify(privs, "Nothing found.");
else if (item == AMBIGUOUS)
notify(privs, "More than one match found.");
strcpy(buff, tprintf("#%d", item));
}
/* --------------------------------------------------------------------------
* Booleans: AND, OR, XOR, NOT
*/
FUNCTION(fun_and)
{
sprintf(buff, "%d", translate(args[0]) && translate(args[1]));
}
FUNCTION(fun_or)
{
sprintf(buff, "%d", translate(args[0]) || translate(args[1]));
}
FUNCTION(fun_not)
{
sprintf(buff, "%d", !translate(args[0]));
}
FUNCTION(fun_xor)
{
sprintf(buff, "%d", ((translate(args[0]) && !translate(args[1])) ||
(!translate(args[0]) && translate(args[1]))));
}
/* --------------------------------------------------------------------------
* Complex list handling functions: SHUFFLE, SORT, SETUNION, SETINTER,
* SETDIFF, and auxiliary functions.
*/
static void swap(p, q)
char **p;
char **q;
{
/* swaps two pointers to strings */
char *temp;
temp = *p;
*p = *q;
*q = temp;
}
FUNCTION(fun_shuffle)
{
/* given a list of words, randomize the order of words.
* We do this by taking each element, and swapping it with another
* element with a greater array index (thus, words[0] can be swapped
* with anything up to words[n], words[5] with anything between
* itself and words[n], etc.
* This is relatively fast - linear time - and reasonably random.
*/
char *words[BUFFER_LEN];
int n, i, j;
/* split the list up, or return if the list is empty */
if (!args[0] || !*args[0]) {
*buff = '\0';
return;
}
n = list2arr(words, BUFFER_LEN, args[0]);
/* shuffle it */
for (i = 0; i < n; i++) {
j = getrandom(n - i) + i;
swap(&words[i], &words[j]);
}
arr2list(words, n, buff);
}
static void do_gensort(s, n, sort_type)
char *s[];
int n;
int sort_type;
{
int i, j;
switch (sort_type) {
case ALPHANUM_LIST:
for (i = 0; i < n; i++)
for (j = i+1; j < n; j++)
if (strcmp(*(s+i), *(s+j)) > 0)
swap(s+i, s+j);
break;
case NUMERIC_LIST:
for (i = 0; i < n; i++) {
for (j = i+1; j < n; j++) {
#ifdef FLOATING_POINTS
if (atof(*(s+i)) > atof(*(s+j)))
swap(s+i, s+j);
#else
if (atoi(*(s+i)) > atoi(*(s+j)))
swap(s+i, s+j);
#endif /* FLOATING_POINTS */
}
}
break;
case DBREF_LIST:
for (i = 0; i < n; i++)
for (j = i+1; j < n; j++)
if (dbnum(*(s+i)) > dbnum(*(s+j)))
swap(s+i, s+j);
break;
}
}
FUNCTION(fun_sort)
{
char list[BUFFER_LEN];
char *ptrs[BUFFER_LEN];
int nptrs, sort_type;
/* Check our arguments. */
if (nargs == 0) {
*buff = '\0';
return;
}
if (!ok_nargs("SORT", nargs, 1, 3, buff))
return;
strcpy(list, args[0]);
nptrs = list2arr(ptrs, BUFFER_LEN, list);
sort_type = get_list_type(args, nargs, 2, ptrs, nptrs);
do_gensort(ptrs, nptrs, sort_type);
arr2list(ptrs, nptrs, buff);
}
static void do_sort(s, n)
char *s[];
int n;
{
/* uses a transposition sort to sort an array of words */
int i, j; /* utility */
for (i = 0; i < n; i++)
for (j = i + 1; j < n; j++)
if (strcmp(*(s + i), *(s + j)) > 0)
swap(s + i, s + j);
}
static void do_setfun(buff, str1, str2, flag)
char *buff;
char *str1;
char *str2;
int flag;
{
/* auxiliary functions to handle list sets */
char list1[BUFFER_LEN], list2[BUFFER_LEN];
char *p, *oldp;
char *a1[MAX_ARG];
char *a2[MAX_ARG];
int n1, n2, x1, x2, val;
/* copy the two lists */
strcpy(list1, str1);
strcpy(list2, str2);
/* make arrays out of them */
n1 = list2arr(a1, BUFFER_LEN, list1);
n2 = list2arr(a2, BUFFER_LEN, list2);
/* sort each array */
do_sort(a1, n1);
do_sort(a2, n2);
x1 = x2 = 0;
oldp = p = buff;
*p = '\0';
/* now do the requested operations */
switch (flag) {
case 1:
/* set union */
/* handle single-word lists that are the same first */
if ((n1 == 1) && (n2 == 1) && !strcmp(a1[0], a2[0])) {
safe_str(a1[0], buff, &p);
break;
}
/* loop until done with at least one list */
while ((x1 < n1) && (x2 < n2)) {
/* skip duplicates */
while ((x1 < n1) && !strcmp(a1[x1], oldp))
x1++;
while ((x2 < n2) && !strcmp(a2[x2], oldp))
x2++;
/* compare and copy */
if ((x1 < n1) && (x2 < n2)) {
if (p != buff)
safe_chr(' ', buff, &p);
oldp = p;
if (strcmp(a1[x1], a2[x2]) < 0) {
safe_str(a1[x1], buff, &p);
x1++;
} else {
safe_str(a2[x2], buff, &p);
x2++;
}
*p = '\0';
}
}
/* copy remainders, and strip duplicates */
for ( ; x1 < n1; x1++) {
if (strcmp(oldp, a1[x1])) {
if (p != buff)
safe_chr(' ', buff, &p);
oldp = p;
safe_str(a1[x1], buff, &p);
*p = '\0';
}
}
for ( ; x2 < n2; x2++) {
if (strcmp(oldp, a2[x2])) {
if (p != buff)
safe_chr(' ', buff, &p);
oldp = p;
safe_str(a2[x2], buff, &p);
*p = '\0';
}
}
break;
case 2:
/* set intersection */
while ((x1 < n1) && (x2 < n2)) {
val = strcmp(a1[x1], a2[x2]);
if (!val) {
/* got a match. copy it */
if (p != buff)
safe_chr(' ', buff, &p);
oldp = p;
safe_str(a1[x1], buff, &p);
x1++;
x2++;
while ((x1 < n1) && !strcmp(a1[x1], oldp))
x1++;
while ((x2 < n2) && !strcmp(a2[x2], oldp))
x2++;
} else if (val < 0) {
x1++;
} else {
x2++;
}
}
break;
case 3:
/* set difference */
while ((x1 < n1) && (x2 < n2)) {
val = strcmp(a1[x1], a2[x2]);
if (!val) {
/* got a match. increment pointers */
oldp = a1[x1];
while ((x1 < n1) && !strcmp(a1[x1], oldp))
x1++;
while ((x2 < n2) && !strcmp(a2[x2], oldp))
x2++;
} else if (val < 0) {
/* found one in first list which isn't in second. copy it */
if (p != buff)
safe_chr(' ', buff, &p);
safe_str(a1[x1], buff, &p);
oldp = a1[x1];
x1++;
while ((x1 < n1) && !strcmp(a1[x1], oldp))
x1++;
} else {
/* found one in second which isn't in first. ignore it */
oldp = a2[x2];
x2++;
while ((x2 < n2) && !strcmp(a2[x2], oldp))
x2++;
}
}
/* copy over the elements of the first list that remain */
while (x1 < n1) {
if (p != buff)
safe_chr(' ', buff, &p);
safe_str(a1[x1], buff, &p);
oldp = a1[x1];
x1++;
while ((x1 < n1) && !strcmp(a1[x1], oldp))
x1++;
}
break;
default:
strcpy(buff, "#-1 BAD ARGUMENT TO SETFUN");
}
*p = '\0';
}
FUNCTION(fun_setunion)
{
if (!*args[0] && !*args[1])
*buff = '\0';
else
do_setfun(buff, args[0], args[1], 1);
}
FUNCTION(fun_setinter)
{
do_setfun(buff, args[0], args[1], 2);
}
FUNCTION(fun_setdiff)
{
do_setfun(buff, args[0], args[1], 3);
}
/* --------------------------------------------------------------------------
* Creation functions: CREATE, OPEN, DIG
*/
FUNCTION(fun_create)
{
sprintf(buff, "#%d", do_create(privs, args[0], atol(args[1])));
}
FUNCTION(fun_open)
{
sprintf(buff, "#%d", do_real_open(privs, args[0], args[1], NOTHING));
}
FUNCTION(fun_dig)
{
char *argv[MAX_ARG];
argv[1] = args[1];
argv[2] = args[2];
sprintf(buff, "#%d", do_dig(privs, args[0], argv, 0));
}
/* --------------------------------------------------------------------------
* The actual function handlers
*/
typedef struct fun FUN;
struct fun {
const char *name;
void (*fun) ();
int nargs;
int ftype;
};
FUN flist[] =
{
{"ABS", fun_abs, 1, FN_REG},
{"ADD", fun_add, 2, FN_REG},
{"AFTER", fun_after, 2, FN_REG},
{"ALPHAMAX", fun_alphamax, -1, FN_REG},
{"ALPHAMIN", fun_alphamin, -1, FN_REG},
{"AND", fun_and, 2, FN_REG},
{"ANDFLAGS", fun_andflags, 2, FN_REG},
{"ART", fun_art, 1, FN_REG},
{"BEEP", fun_beep, 1, FN_REG},
{"BEFORE", fun_before, 2, FN_REG},
{"CAPSTR", fun_capstr, 1, FN_REG},
{"CAT", fun_cat, -1, FN_REG},
{"COMP", fun_comp, 2, FN_REG},
{"CON", fun_con, 1, FN_REG},
{"CONN", fun_conn, 1, FN_REG},
{"CONTROLS", fun_controls, 2, FN_REG},
{"CONVSECS", fun_convsecs, 1, FN_REG},
{"CONVTIME", fun_convtime, 1, FN_REG},
{"CREATE", fun_create, 2, FN_REG},
{"DELETE", fun_delete, -1, FN_REG},
{"DIE", fun_die, 2, FN_REG},
{"DIG", fun_dig, 3, FN_REG},
{"DIST2D", fun_dist2d, 4, FN_REG},
{"DIST3D", fun_dist3d, 6, FN_REG},
{"DIV", fun_div, 2, FN_REG},
{"EDIT", fun_edit, 3, FN_REG},
{"ELEMENT", fun_element, 3, FN_REG},
{"ELOCK", fun_elock, 2, FN_REG},
{"EQ", fun_eq, 2, FN_REG},
{"EVAL", fun_eval, 2, FN_REG},
{"ESCAPE", fun_escape, 1, FN_REG},
{"EXIT", fun_exit, 1, FN_REG},
{"EXTRACT", fun_extract, 3, FN_REG},
{"FILTER", fun_filter, 2, FN_REG},
{"FIRST", fun_first, 1, FN_REG},
{"FLAGS", fun_flags, 1, FN_REG},
{"FLIP", fun_flip, 1, FN_REG},
{"FOLD", fun_fold, -1, FN_REG},
{"FULLNAME", fun_fullname, 1, FN_REG},
{"GET", fun_get, 1, FN_REG},
{"GET_EVAL", fun_ufun, -1, FN_REG},
{"GREP", fun_grep, 3, FN_REG},
{"GT", fun_gt, 2, FN_REG},
{"GTE", fun_gte, 2, FN_REG},
{"HASFLAG", fun_hasflag, 2, FN_REG},
{"HOME", fun_home, 1, FN_REG},
{"IDLE", fun_idlesecs, 1, FN_REG},
{"IDLESECS", fun_idlesecs, 1, FN_REG},
{"INDEX", fun_index, 4, FN_REG},
{"INSERT", fun_insert, -1, FN_REG},
{"ISNUM", fun_isnum, 1, FN_REG},
{"ISWORD", fun_isword, 1, FN_REG},
{"ITER", fun_iter, 2, FN_NOPARSE},
{"ITEMS", fun_items, 2, FN_REG},
{"LATTR", fun_lattr, 1, FN_REG},
{"LCON", fun_lcon, 1, FN_REG},
{"LCSTR", fun_lcstr, 1, FN_REG},
{"LEXITS", fun_lexits, 1, FN_REG},
{"LJUST", fun_ljust, -1, FN_REG},
{"LNUM", fun_lnum, 1, FN_REG},
{"LOC", fun_loc, 1, FN_REG},
{"LOCATE", fun_locate, 3, FN_REG},
{"LOCK", fun_lock, 1, FN_REG},
{"LSEARCH", fun_lsearch, 3, FN_REG},
{"LSTATS", fun_lstats, 1, FN_REG},
{"LT", fun_lt, 2, FN_REG},
{"LTE", fun_lte, 2, FN_REG},
{"LWHO", fun_lwho, 0, FN_REG},
#ifdef USE_MAILER
{"MAIL", fun_mail, -1, FN_REG},
#endif
{"MATCH", fun_match, 2, FN_REG},
{"MAX", fun_max, -1, FN_REG},
{"MEMBER", fun_member, 2, FN_REG},
{"MERGE", fun_merge, 3, FN_REG},
{"MID", fun_mid, 3, FN_REG},
{"MIN", fun_min, -1, FN_REG},
{"MOD", fun_mod, 2, FN_REG},
{"MONEY", fun_money, 1, FN_REG},
{"MUL", fun_mul, 2, FN_REG},
{"NAME", fun_name, 1, FN_REG},
{"NEARBY", fun_nearby, 2, FN_REG},
{"NEQ", fun_neq, 2, FN_REG},
{"NEXT", fun_next, 1, FN_REG},
{"NOT", fun_not, 1, FN_REG},
{"NUM", fun_num, 1, FN_REG},
{"OBJ", fun_obj, 1, FN_REG},
{"OPEN", fun_open, 2, FN_REG},
{"OR", fun_or, 2, FN_REG},
{"ORFLAGS", fun_orflags, 2, FN_REG},
{"OWNER", fun_owner, 1, FN_REG},
{"PARENT", fun_parent, 1, FN_REG},
{"PMATCH", fun_pmatch, 1, FN_REG},
{"POS", fun_pos, 2, FN_REG},
{"POSS", fun_poss, 1, FN_REG},
{"R", fun_r, 1, FN_REG},
{"RAND", fun_rand, 1, FN_REG},
{"REMOVE", fun_remove, 2, FN_REG},
{"REPEAT", fun_repeat, 2, FN_REG},
{"REPLACE", fun_replace, -1, FN_REG},
{"REST", fun_rest, 1, FN_REG},
{"REVERSE", fun_flip, 1, FN_REG},
{"REVWORDS", fun_revwords, 1, FN_REG},
{"RJUST", fun_rjust, -1, FN_REG},
#ifdef DO_GLOBALS
{"RNUM", fun_rnum, 2, FN_REG},
#endif
{"ROOM", fun_room, 1, FN_REG},
{"S", fun_s, 1, FN_REG},
{"SECS", fun_secs, 0, FN_REG},
{"SECURE", fun_secure, 1, FN_REG},
{"SETQ", fun_setq, 2, FN_REG},
{"SETDIFF", fun_setdiff, 2, FN_REG},
{"SETINTER", fun_setinter, 2, FN_REG},
{"SETUNION", fun_setunion, 2, FN_REG},
{"SHUFFLE", fun_shuffle, 1, FN_REG},
{"SIGN", fun_sign, 1, FN_REG},
{"SORT", fun_sort, -1, FN_REG},
{"SPACE", fun_space, 1, FN_REG},
{"SPLICE", fun_splice, 3, FN_REG},
{"SQUISH", fun_squish, 1, FN_REG},
{"SCRAMBLE", fun_scramble, 1, FN_REG},
{"STRCAT", fun_strcat, 2, FN_REG},
{"STRLEN", fun_strlen, 1, FN_REG},
{"STRMATCH", fun_strmatch, 2, FN_REG},
{"SUB", fun_sub, 2, FN_REG},
{"SUBJ", fun_subj, 1, FN_REG},
{"SWITCH", fun_switch, -1, FN_NOPARSE},
{"TIME", fun_time, 0, FN_REG},
{"TRUNC", fun_trunc, 1, FN_REG},
{"TYPE", fun_type, 1, FN_REG},
{"UCSTR", fun_ucstr, 1, FN_REG},
{"UFUN", fun_ufun, -1, FN_REG},
{"U", fun_ufun, -1, FN_REG},
{"V", fun_v, 1, FN_REG},
{"VAL", fun_trunc, 1, FN_REG},
{"VISIBLE", fun_visible, 2, FN_REG},
{"WHERE", fun_where, 1, FN_REG},
{"WORDPOS", fun_wordpos, 2, FN_REG},
{"WORDS", fun_words, 1, FN_REG},
{"XGET", fun_xget, 2, FN_REG},
{"XOR", fun_xor, 2, FN_REG},
{"ZFUN", fun_zfun, -1, FN_REG},
{"ZONE", fun_zone, 1, FN_REG},
#ifdef FLOATING_POINTS
{"ACOS", fun_acos, 1, FN_REG},
{"ASIN", fun_asin, 1, FN_REG},
{"ATAN", fun_atan, 1, FN_REG},
{"CEIL", fun_ceil, 1, FN_REG},
{"COS", fun_cos, 1, FN_REG},
{"E", fun_e, 0, FN_REG},
{"EXP", fun_exp, 1, FN_REG},
{"FDIV", fun_fdiv, 2, FN_REG},
{"FLOOR", fun_floor, 1, FN_REG},
{"LOG", fun_log, 1, FN_REG},
{"LN", fun_ln, 1, FN_REG},
{"PI", fun_pi, 0, FN_REG},
{"POWER", fun_power, 2, FN_REG},
{"ROUND", fun_round, 2, FN_REG},
{"SIN", fun_sin, 1, FN_REG},
{"TAN", fun_tan, 1, FN_REG},
#endif /* FLOATING_POINTS */
{NULL, NULL, 0, 0}
};
void do_list_functions(player)
dbref player;
{
/* lists all built-in functions. */
FUN *fp;
char buff[BUFFER_LEN];
char *bp;
bp = buff;
safe_str("Functions:", buff, &bp);
for (fp = flist; fp->name != NULL; fp++) {
safe_chr(' ', buff, &bp);
safe_str(fp->name, buff, &bp);
}
*bp = '\0';
notify(player, buff);
}
/*---------------------------------------------------------------------------
* Hashed function table stuff
*/
#define FUNC_HASH_SIZE 128
#define FUNC_HASH_MASK 127
struct ftab_entry {
struct ftab_entry *next;
char *name;
FUN *fp;
};
static struct ftab_entry *func_hashtab[FUNC_HASH_SIZE];
static FUN *null_htab;
FUN *func_hash_lookup(name)
char *name;
{
struct ftab_entry *p;
for (p = func_hashtab[hash_fn(name, FUNC_HASH_MASK)];
p != NULL; p = p->next)
if (!strcmp(name, p->name))
return p->fp;
return null_htab; /* the notfound pointer */
}
void func_hash_insert(name, func)
char *name;
FUN *func;
{
struct ftab_entry *newp;
unsigned hashval;
/* can assume no duplicates */
newp = (struct ftab_entry *) malloc(sizeof(struct ftab_entry));
newp->name = (char *) strdup(name);
newp->fp = func;
hashval = hash_fn(name, FUNC_HASH_MASK);
newp->next = func_hashtab[hashval];
func_hashtab[hashval] = newp;
}
void init_func_hashtab()
{
FUN *fp;
for (fp = flist; fp->name; fp++)
func_hash_insert((char *)fp->name, (FUN *)fp);
/* treat null function as a special case */
null_htab = (FUN *) malloc(sizeof(FUN));
null_htab->name = NULL;
null_htab->fun = NULL;
null_htab->nargs = 0;
}
/*-------------------------------------------------------------------------
* Function handlers and the other good stuff. Almost all this code is
* a modification of TinyMUSH 2.0 code.
*/
int extra_arglist(str, fargs, nargs)
char *str;
char *fargs[];
int nargs;
{
/* parses apart a comma-separated list, and returns the number of
* arguments obtained. The list should already be pronoun-substituted.
* str is destructively modified. No memory is allocated. No more than
* nargs arguments are made. The list elements are placed in fargs.
*/
int i;
for (i = 0; i < nargs; i++)
fargs[i] = NULL;
if (!str || !*str)
return 0;
i = 0;
while ((i < nargs) && str) {
if (i < (nargs - 1))
fargs[i] = parse_to(&str, ',', 0);
else
fargs[i] = parse_to(&str, '\0', 0);
i++;
}
return i;
}
int get_gender(player)
dbref player;
{
/* 0 for error, 1 for neuter, 2 for female, 3 for male */
ATTR *a;
a = atr_get(player, "SEX");
if (!a)
return 1;
switch (*uncompress(a->value)) {
case 'M':
case 'm':
return 3;
case 'f':
case 'F':
return 2;
default:
return 1;
}
}
#define NEXTCHAR \
if (cstr == zstr) { \
cstr++; \
zstr++; \
} else \
*zstr++ = *cstr++
char *parse_to(dstr, delim, eflags)
char **dstr;
char delim;
int eflags;
{
/* Split up a line at a character, but obey nesting. This will hack
* up the string (null replaces the delimiter)
* str will be modified to point to the character after the delimiter.
* The function will return a pointer to the found string.
* If we don't find the delimiter, str is returned as NULL.
*/
#define MAX_STACK 32
char stack[MAX_STACK];
char *rstr, *cstr, *zstr;
int sp, tp, first, brlev;
if (!dstr || !*dstr)
return NULL;
if (**dstr == '\0') {
rstr = *dstr;
*dstr = NULL;
return rstr;
}
sp = 0;
first = 1;
rstr = *dstr;
while (*rstr && isspace(*rstr))
rstr++;
*dstr = rstr;
zstr = cstr = rstr;
while (*cstr) {
switch (*cstr) {
case '\\': /* escape char */
case '%': /* can be used as an escape char */
first = 0;
NEXTCHAR;
if (*cstr)
NEXTCHAR;
break;
case ']':
case ')':
first = 0;
for (tp = sp - 1; (tp >= 0) && (stack[tp] != *cstr); tp--)
;
/* If we've hit something on a stack, go back to it.
* Otherwise, if it's our delimiter, we're done (replace
* delim with null, and return a pointer to the char after
* it). If it's not, then just move over it.
*/
if (tp >= 0)
sp = tp;
else
if (*cstr == delim) {
if (!first && (cstr[-1] == ' '))
zstr--;
*zstr = '\0';
*dstr = ++cstr;
return rstr;
}
NEXTCHAR;
break;
case '{':
first = 0;
brlev = 1;
if (eflags & EV_STRIP) {
cstr++;
} else {
NEXTCHAR;
}
while (*cstr && (brlev > 0)) {
switch (*cstr) {
case '\\':
case '%':
if (cstr[1]) {
NEXTCHAR;
}
break;
case '{':
brlev++;
break;
case '}':
brlev--;
break;
}
if (brlev > 0) {
NEXTCHAR;
}
}
if ((eflags & EV_STRIP) && (brlev == 0)) {
cstr++;
} else if (brlev == 0) {
NEXTCHAR;
}
break;
default:
if ((*cstr == delim) && (sp == 0)) {
if (!first && (cstr[-1] == ' '))
zstr--;
*zstr = '\0';
*dstr = ++cstr;
return rstr;
}
switch (*cstr) {
case ' ': /* just a space. kill extra ones. */
if (first)
rstr++;
else if (cstr[-1] == ' ')
zstr--;
break;
case '[':
first = 0;
if (sp < MAX_STACK)
stack[sp++] = ']';
break;
case '(':
first = 0;
if (sp < MAX_STACK)
stack[sp++] = ')';
break;
default:
first = 0;
}
NEXTCHAR;
}
}
if (!first && (cstr[-1] == ' '))
zstr--;
*zstr = '\0';
*dstr = NULL;
return rstr;
}
char *parse_arglist(player, cause, dstr, delim, eflags, fargs, nfargs)
dbref player;
dbref cause;
char *dstr;
char delim;
int eflags;
char *fargs[];
int nfargs;
{
/* Parses a line of comma-separated strings into an argument list.
* A pointer is returned to whatever follows the final delimiter
* (or NULL if the arglist is unterminated).
* The original arglist (in dstr) is hacked up.
* This function allocates memory for the fargs, which must be freed.
*/
char *rstr, *tstr;
int arg, peval;
for (arg = 0; arg < nfargs; arg++)
fargs[arg] = NULL;
if (!dstr)
return NULL;
rstr = parse_to(&dstr, delim, 0);
arg = 0;
if (eflags & EV_EVAL)
peval = 0;
else
peval = eflags;
while ((arg < nfargs) && rstr) {
if (arg < (nfargs - 1))
tstr = parse_to(&rstr, ',', peval);
else
tstr = parse_to(&rstr, '\0', peval);
if (eflags & EV_EVAL) {
fargs[arg] = exec(player, cause, eflags | EV_FCHECK, tstr);
} else {
fargs[arg] = (char *) malloc(BUFFER_LEN + 1);
#ifdef MEM_CHECK
add_check("exec.buff");
#endif
strcpy(fargs[arg], tstr);
}
arg++;
}
return dstr;
}
char *exec(player, cause, eflags, str)
dbref player;
dbref cause;
int eflags;
char *str;
{
/* Function and other substitution evaluation.
* This function mallocs memory, which must be freed.
*/
char *fargs[10];
char *buff, *bufc, *tstr, *tbuf, *tbufc, *savepos;
char savec, ch;
int at_space, nfargs, gender, i, j, done;
FUN *fp;
ATTR *attrib;
char temp[32];
int a;
char dbuf[BUFFER_LEN];
char *dp, *dpexec;
char dsave[BUFFER_LEN];
static const char *subj[4] = {"", "it", "she", "he"};
static const char *poss[4] = {"", "its", "her", "his"};
static const char *obj[4] = {"", "it", "her", "him"};
if (str == NULL)
return NULL;
for (a = 0; a < 10; a++)
fargs[a] = NULL;
buff = (char *) malloc(BUFFER_LEN + 1);
#ifdef MEM_CHECK
add_check("exec.buff");
#endif
bufc = buff;
at_space = 1;
gender = -1;
done = 0;
if (Debug(player)) {
dp = dbuf;
safe_str(tprintf("#%d! %s => ", player, str), dbuf, &dp);
Flags(player) &= ~DEBUGGING;
strcpy(dsave, str);
dpexec = exec(player, cause, eflags, dsave);
safe_str(dpexec, dbuf, &dp);
*dp = '\0';
free(dpexec);
Flags(player) |= DEBUGGING;
raw_notify(Owner(player), dbuf);
}
while (*str && !done) {
switch (*str) {
case ' ':
/* Just a space. Add a space if previous char wasn't a space */
if (!at_space) {
safe_chr(' ', buff, &bufc);
at_space = 1;
}
break;
case '\\':
/* The escape char. Add the next char without processing it. */
at_space = 0;
str++;
if (*str)
safe_chr(*str, buff, &bufc);
else
str--;
break;
case '[':
/* Beginning of a function. Evaluate the contents of this
* as a function. If we find no closing bracket, just add
* the '[' and go on.
*/
at_space = 0;
tstr = str++;
tbuf = parse_to(&str, ']', 0);
if (str == NULL) {
safe_chr('[', buff, &bufc);
str = tstr;
} else {
tstr = exec(player, cause, eflags | EV_FCHECK | EV_FMAND, tbuf);
safe_str(tstr, buff, &bufc);
free(tstr);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
str--;
}
break;
case '{':
/* Start of something. Copy everything up to the terminating '}',
* don't parse, unless there's no closing brace, in which case
* we just add the '{'.
*/
at_space = 0;
tstr = str++;
tbuf = parse_to(&str, '}', 0);
if (str == NULL) {
safe_chr('{', buff, &bufc);
str = tstr;
} else {
if (!(eflags & EV_STRIP)) {
safe_chr('{', buff, &bufc);
}
tstr = exec(player, cause, (eflags & ~(EV_STRIP | EV_FCHECK)), tbuf);
safe_str(tstr, buff, &bufc);
if (!(eflags & EV_STRIP)) {
safe_chr('}', buff, &bufc);
}
free(tstr);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
str--;
}
break;
case '%':
/* Start of percent substitution. Evaluate the chars following,
* doing the substitution.
*/
at_space = 0;
str++;
savec = *str;
savepos = bufc;
switch (savec) {
case '\0': /* Null -- we're done */
str--;
break;
case '%': /* Sequence is '%%', so a real percent */
safe_chr('%', buff, &bufc);
break;
case 'r': /* Newline */
case 'R':
safe_str((char *)"\r\n", buff, &bufc);
break;
case 't': /* Tab */
case 'T':
safe_chr('\t', buff, &bufc);
break;
case 'b': /* Blank space */
case 'B':
safe_chr(' ', buff, &bufc);
break;
case '0': /* stack */
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
i = (*str - '0');
if (wptr[i] != NULL)
safe_str(wptr[i], buff, &bufc);
break;
case 'v': /* variable argument */
case 'V':
case 'w':
case 'W':
case 'x':
case 'X':
str++;
if (!*str)
str--;
ch = UPCASE(*str);
if (!isalpha(ch))
break;
attrib = atr_get(player, tprintf("%c%c", UPCASE(*(str-1)), ch));
if (attrib)
safe_str(uncompress(attrib->value), buff, &bufc);
break;
case 'o': /* objective pronoun */
case 'O':
if (gender < 0)
gender = get_gender(cause);
tbuf = (char *) obj[gender];
safe_str(tbuf, buff, &bufc);
break;
case 'p':
case 'P': /* possessive pronoun */
if (gender < 0)
gender = get_gender(cause);
tbuf = (char *) poss[gender];
safe_str(tbuf, buff, &bufc);
break;
case 's': /* subjective pronoun */
case 'S':
if (gender < 0)
gender = get_gender(cause);
tbuf = (char *) subj[gender];
safe_str(tbuf, buff, &bufc);
break;
case 'n': /* name of enactor */
case 'N':
safe_str(Name(cause), buff, &bufc);
break;
case '#': /* enactor dbref */
sprintf(temp, "#%d", cause);
safe_str(temp, buff, &bufc);
break;
case '!': /* executor ("me") dbref */
sprintf(temp, "#%d", player);
safe_str(temp, buff, &bufc);
break;
case 'l': /* location of enactor */
case 'L':
/* this does not violate security because you have to trigger
* something somehow (for example, by a $command) in order for
* it to find your location.
*/
sprintf(temp, "#%d", getloc(cause));
safe_str(temp, buff, &bufc);
break;
default: /* just copy */
safe_chr(*str, buff, &bufc);
}
if (isupper(savec))
*savepos = UPCASE(*savepos);
break;
case '(':
/* start of argument list. Check to see if the stuff before is
* a function. If so, execute it (if we should).
*/
at_space = 0;
if (!(eflags & EV_FCHECK)) {
safe_chr('(', buff, &bufc);
break;
}
eflags &= ~EV_FCHECK;
/* get uppercase version of the function name, check to see if
* it exists.
*/
*bufc = '\0';
tbuf = (char *) malloc(SBUF_LEN + 1);
#ifdef MEM_CHECK
add_check("exec.function_name");
#endif
tbufc = tbuf;
safe_short_str(buff, tbuf, &tbufc);
*tbufc = '\0';
while ((--tbufc >= tbuf) && isspace(*tbufc))
;
tbufc++;
for (tbufc = tbuf; *tbufc; tbufc++)
*tbufc = UPCASE(*tbufc);
fp = func_hash_lookup(tbuf);
/* give error message if function not found */
if (!fp->name) {
if (eflags & EV_FMAND) {
bufc = buff;
safe_str((char *) "#-1 FUNCTION (", buff, &bufc);
safe_str(tbuf, buff, &bufc);
safe_str((char *) ") NOT FOUND", buff, &bufc);
done = 1;
} else {
safe_chr('(', buff, &bufc);
}
free(tbuf);
#ifdef MEM_CHECK
del_check("exec.function_name");
#endif
break;
}
free(tbuf);
#ifdef MEM_CHECK
del_check("exec.function_name");
#endif
/* get the arglist */
nfargs = 10;
tstr = (char *) str;
str = parse_arglist(player, cause, str + 1, ')',
(fp->ftype == FN_NOPARSE) ?
(eflags & ~EV_EVAL) : (eflags | EV_EVAL),
fargs, nfargs);
/* if there's no closing delimiter, just add the '(' and continue */
if (!str) {
str = tstr;
safe_chr(*str, buff, &bufc);
for (i = 0; i < nfargs; i++)
if (fargs[i] != NULL) {
free(fargs[i]);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
}
break;
}
/* Count number of args returned */
str--;
j = 0;
for (i = 0; i < nfargs; i++)
if (fargs[i] != NULL)
j = i + 1;
nfargs = j;
/* If we have the right number of args, eval the function.
* Otherwise, return an error message.
* Special case: zero args is return by parse_arglist as
* one null arg.
*/
if ((fp->nargs == 0) && (nfargs == 1)) {
if (!*fargs[0]) {
free(fargs[0]);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
fargs[0] = NULL;
nfargs = 0;
}
}
if ((nfargs == fp->nargs) || (fp->nargs == -1)) {
/* Check recursion limit first */
recurs_lev++;
invok_counter++;
if (recurs_lev >= MAX_NEST_LEVEL)
strcpy(buff, "#-1 FUNCTION RECURSION LIMIT EXCEEDED");
else if (invok_counter > MAX_NEST_LEVEL * 100)
strcpy(buff, "#-1 FUNCTION INVOCATION LIMIT EXCEEDED");
else if (invok_counter < MAX_NEST_LEVEL * 100) {
if (fp->fun != fun_gfun)
fp->fun(buff, fargs, nfargs, player, cause);
else
fp->fun(buff, fargs, nfargs, player, cause,
GF_Index(fp->ftype));
} else {
*bufc = '\0';
}
for (bufc = buff; *bufc; bufc++) /* fix bufc */
;
recurs_lev--; /* don't decrement the invok_counter! */
} else {
bufc = buff;
tstr = (char *) malloc(SBUF_LEN + 1);
#ifdef MEM_CHECK
add_check("exec.numargs");
#endif
sprintf(tstr, "%d", fp->nargs);
safe_str((char *) "#-1 FUNCTION (", buff, &bufc);
safe_str((char *) fp->name, buff, &bufc);
safe_str((char *) ") EXPECTS ", buff, &bufc);
safe_str(tstr, buff, &bufc);
safe_str((char *) " ARGUMENTS", buff, &bufc);
free(tstr);
#ifdef MEM_CHECK
del_check("exec.numargs");
#endif
}
/* free up the space allocated for the args */
for (i = 0; i < nfargs; i++)
if (fargs[i] != NULL) {
free(fargs[i]);
#ifdef MEM_CHECK
del_check("exec.buff");
#endif
}
break;
default:
/* just another character. copy it. */
at_space = 0;
safe_chr(*str, buff, &bufc);
}
str++;
}
*bufc = '\0';
return buff;
}
char *strip_braces(line)
char *line;
{
/* this is a hack which just strips a level of braces. It malloc()s memory
* which must be free()d later.
*/
char *buff;
char *tstr, *tbuf;
char *bufc;
char *str = line;
buff = (char *) malloc(BUFFER_LEN + 1);
#ifdef MEM_CHECK
add_check("strip_braces.buff");
#endif
bufc = buff;
while (isspace(*str)) /* eat spaces at the beginning */
str++;
switch (*str) {
case '{':
tstr = str++;
tbuf = parse_to(&str, '}', 0);
if (str == NULL) {
str = tstr;
safe_str(tstr, buff, &bufc);
*bufc = '\0';
return buff;
} else {
safe_str(tbuf, buff, &bufc);
str--;
*bufc = '\0';
return buff;
}
break; /* NOT REACHED */
default:
strcpy(buff, str);
return buff;
}
}
/*------------------------------------------------------------------------
* User-defined global function handlers
*/
typedef struct userfn_entry USERFN_ENTRY;
struct userfn_entry {
dbref thing;
char *name;
};
static USERFN_ENTRY userfn_tab[MAX_GLOBAL_FNS];
static int userfn_count = 0;
GLOBAL_FUN(fun_gfun)
{
/* this function can never be called directly. It is called by
* user-defined functions.
*/
ATTR *attrib;
attrib = atr_get(userfn_tab[fn_num].thing, userfn_tab[fn_num].name);
do_userfn(privs, doer, userfn_tab[fn_num].thing, attrib, args, buff, 1);
}
void do_function(player, name, argv)
dbref player;
char *name;
char *argv[];
{
/* command of format: @function <function name>=<thing>,<attribute>
* Adds a new user-defined function.
*/
int i;
struct ftab_entry *tabp;
char tbuf1[BUFFER_LEN];
char *bp = tbuf1;
dbref thing;
FUN *fp;
/* if no arguments, just give the list of user functions, by walking
* the function hash table, and looking up all functions marked
* as user-defined.
*/
if (!name || !*name) {
if (userfn_count == 0) {
notify(player, "No global user-defined functions exist.");
return;
}
if (Global_Funcs(player)) {
/* if the player is privileged, display user-def'ed functions
* with corresponding dbref number of thing and attribute name.
*/
notify(player,
"Function Name Dbref # Attrib");
for (i = 0; i < FUNC_HASH_SIZE; i++) {
for (tabp = func_hashtab[i]; tabp != NULL; tabp = tabp->next) {
if (tabp->fp->ftype >= GLOBAL_OFFSET)
notify(player,
tprintf("%-32s %6d %s", tabp->fp->name,
userfn_tab[GF_Index(tabp->fp->ftype)].thing,
userfn_tab[GF_Index(tabp->fp->ftype)].name));
}
}
} else {
/* just print out the list of available functions */
safe_str("User functions:", tbuf1, &bp);
for (i = 0; i < FUNC_HASH_SIZE; i++) {
for (tabp = func_hashtab[i]; tabp != NULL; tabp = tabp->next) {
if (tabp->fp->ftype >= GLOBAL_OFFSET) {
safe_chr(' ', tbuf1, &bp);
safe_str(tabp->fp->name, tbuf1, &bp);
}
}
}
*bp = '\0';
notify(player, tbuf1);
}
return;
}
/* otherwise, we are adding a user function. There is NO deletion
* mechanism. Only those with the Global_Funcs power may add stuff.
* If you add a function that is already a user-defined function,
* the old function gets over-written.
*/
if (!Global_Funcs(player)) {
notify(player, "Permission denied.");
return;
}
if (userfn_count >= MAX_GLOBAL_FNS) {
notify(player, "Function table full.");
return;
}
if (!argv[1] || !*argv[1] || !argv[2] || !*argv[2]) {
notify(player, "You must specify an object and an attribute.");
return;
}
/* make sure the function name length is okay */
if (strlen(name) > 30) {
notify(player, "Function name too long.");
return;
}
/* find the object. For some measure of security, the player must
* be able to examine it.
*/
init_match(player, argv[1], NOTYPE);
match_everything();
if ((thing = noisy_match_result()) == NOTHING)
return;
if (!Can_Examine(player, thing)) {
notify(player, "No permission to examine object.");
return;
}
/* we don't need to check if the attribute exists. If it doesn't,
* it's not our problem - it's the user's responsibility to make
* sure that the attribute exists (if it doesn't, invoking the
* function will return a #-1 NO SUCH ATTRIBUTE error).
* We do, however, need to make sure that the user isn't trying
* to replace a built-in function.
*/
fp = func_hash_lookup(upcasestr(name));
if (fp->name == NULL) {
/* a completely new entry. First, insert it into general hash table */
fp = (FUN *) malloc(sizeof(FUN));
fp->name = (char *) strdup(name);
fp->fun = fun_gfun;
fp->nargs = -1;
fp->ftype = userfn_count + GLOBAL_OFFSET;
func_hash_insert(name, (FUN *)fp);
/* now add it to the user function table */
userfn_tab[userfn_count].thing = thing;
userfn_tab[userfn_count].name = (char *) strdup(upcasestr(argv[2]));
userfn_count++;
notify(player, "Function added.");
return;
} else {
/* we are modifying an old entry */
if ((fp->ftype == FN_REG) || (fp->ftype == FN_NOPARSE)) {
notify(player, "You cannot change a built-in function.");
return;
}
userfn_tab[GF_Index(fp->ftype)].thing = thing;
free(userfn_tab[GF_Index(fp->ftype)].name);
userfn_tab[GF_Index(fp->ftype)].name =
(char *)strdup(upcasestr(argv[2]));
notify(player, "Function updated.");
}
}